From 16a19267cfe24ba2ecd9256e41c5f79289db3980 Mon Sep 17 00:00:00 2001 From: Alan Yee Date: Thu, 29 Aug 2019 15:01:54 -0700 Subject: [PATCH 1/5] Update ftplib.py Enforce PEP 257 conventions --- Lib/ftplib.py | 152 ++++++++++++++++++++++++++------------------------ 1 file changed, 80 insertions(+), 72 deletions(-) diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 58a46bca4a3671..070b5d2b758fa4 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -72,8 +72,7 @@ class error_proto(Error): pass # response does not begin with [1-5] # The class itself class FTP: - - '''An FTP client class. + """An FTP client class. To create a connection, call the class using these arguments: host, user, passwd, acct, timeout @@ -93,8 +92,7 @@ class FTP: below for details). The download/upload functions first issue appropriate TYPE and PORT or PASV commands. - ''' - + """ debugging = 0 host = '' port = FTP_PORT @@ -105,12 +103,13 @@ class FTP: passiveserver = 1 encoding = "latin-1" - # Initialization method (called by class instantiation). - # Initialize host to localhost, port to standard ftp port - # Optional arguments are host (for connect()), - # and user, passwd, acct (for login()) def __init__(self, host='', user='', passwd='', acct='', timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None): + """Initialization method (called by class instantiation). + Initialize host to localhost, port to standard ftp port + Optional arguments are host (for connect()), + and user, passwd, acct (for login()) + """ self.source_address = source_address self.timeout = timeout if host: @@ -121,8 +120,8 @@ def __init__(self, host='', user='', passwd='', acct='', def __enter__(self): return self - # Context management protocol: try to quit() if active def __exit__(self, *args): + """Context management protocol: try to quit() if active""" if self.sock is not None: try: self.quit() @@ -133,13 +132,13 @@ def __exit__(self, *args): self.close() def connect(self, host='', port=0, timeout=-999, source_address=None): - '''Connect to host. Arguments are: + """Connect to host. Arguments are: - host: hostname to connect to (string, default previous host) - port: port to connect to (integer, default previous port) - timeout: the timeout to set against the ftp socket(s) - source_address: a 2-tuple (host, port) for the socket to bind to as its source address before connecting. - ''' + """ if host != '': self.host = host if port > 0: @@ -157,36 +156,39 @@ def connect(self, host='', port=0, timeout=-999, source_address=None): return self.welcome def getwelcome(self): - '''Get the welcome message from the server. - (this is read and squirreled away by connect())''' + """Get the welcome message from the server. + (this is read and squirreled away by connect()) + """ if self.debugging: print('*welcome*', self.sanitize(self.welcome)) return self.welcome def set_debuglevel(self, level): - '''Set the debugging level. + """Set the debugging level. The required argument level means: 0: no debugging output (default) 1: print commands and responses but not body text etc. - 2: also print raw lines read and sent before stripping CR/LF''' + 2: also print raw lines read and sent before stripping CR/LF + """ self.debugging = level debug = set_debuglevel def set_pasv(self, val): - '''Use passive or active mode for data transfers. + """Use passive or active mode for data transfers. With a false argument, use the normal PORT mode, - With a true argument, use the PASV command.''' + With a true argument, use the PASV command. + """ self.passiveserver = val - # Internal: "sanitize" a string for printing def sanitize(self, s): + """Internal: 'sanitize' a string for printing""" if s[:5] in {'pass ', 'PASS '}: i = len(s.rstrip('\r\n')) s = s[:5] + '*'*(i-5) + s[i:] return repr(s) - # Internal: send one line to the server, appending CRLF def putline(self, line): + """Internal: send one line to the server, appending CRLF""" if '\r' in line or '\n' in line: raise ValueError('an illegal newline character should not be contained') sys.audit("ftplib.sendcmd", self, line) @@ -195,14 +197,15 @@ def putline(self, line): print('*put*', self.sanitize(line)) self.sock.sendall(line.encode(self.encoding)) - # Internal: send one command to the server (through putline()) def putcmd(self, line): + """Internal: send one command to the server (through putline())""" if self.debugging: print('*cmd*', self.sanitize(line)) self.putline(line) - # Internal: return one line from the server, stripping CRLF. - # Raise EOFError if the connection is closed def getline(self): + """Internal: return one line from the server, stripping CRLF. + Raise EOFError if the connection is closed + """ line = self.file.readline(self.maxline + 1) if len(line) > self.maxline: raise Error("got more than %d bytes" % self.maxline) @@ -216,11 +219,13 @@ def getline(self): line = line[:-1] return line - # Internal: get a response from the server, which may possibly - # consist of multiple lines. Return a single string with no - # trailing CRLF. If the response consists of multiple lines, - # these are separated by '\n' characters in the string + # def getmultiline(self): + """Internal: get a response from the server, which may possibly + consist of multiple lines. Return a single string with no + trailing CRLF. If the response consists of multiple lines, + these are separated by '\n' characters in the string + """ line = self.getline() if line[3:4] == '-': code = line[:3] @@ -232,9 +237,10 @@ def getmultiline(self): break return line - # Internal: get a response from the server. - # Raise various errors if the response indicates an error def getresp(self): + """Internal: get a response from the server. + Raise various errors if the response indicates an error + """ resp = self.getmultiline() if self.debugging: print('*resp*', self.sanitize(resp)) @@ -256,10 +262,11 @@ def voidresp(self): return resp def abort(self): - '''Abort a file transfer. Uses out-of-band data. + """Abort a file transfer. Uses out-of-band data. This does not follow the procedure from the RFC to send Telnet IP and Synch; that doesn't seem to work with the servers I've - tried. Instead, just send the ABOR command as OOB data.''' + tried. Instead, just send the ABOR command as OOB data. + """ line = b'ABOR' + B_CRLF if self.debugging > 1: print('*put urgent*', self.sanitize(line)) @@ -270,7 +277,7 @@ def abort(self): return resp def sendcmd(self, cmd): - '''Send a command and return the response.''' + """Send a command and return the response.""" self.putcmd(cmd) return self.getresp() @@ -280,9 +287,9 @@ def voidcmd(self, cmd): return self.voidresp() def sendport(self, host, port): - '''Send a PORT command with the current host and the given + """Send a PORT command with the current host and the given port number. - ''' + """ hbytes = host.split('.') pbytes = [repr(port//256), repr(port%256)] bytes = hbytes + pbytes @@ -290,7 +297,7 @@ def sendport(self, host, port): return self.voidcmd(cmd) def sendeprt(self, host, port): - '''Send an EPRT command with the current host and the given port number.''' + """Send an EPRT command with the current host and the given port number.""" af = 0 if self.af == socket.AF_INET: af = 1 @@ -303,7 +310,7 @@ def sendeprt(self, host, port): return self.voidcmd(cmd) def makeport(self): - '''Create a new socket and send a PORT command for it.''' + """Create a new socket and send a PORT command for it.""" sock = socket.create_server(("", 0), family=self.af, backlog=1) port = sock.getsockname()[1] # Get proper port host = self.sock.getsockname()[0] # Get proper host @@ -382,7 +389,7 @@ def transfercmd(self, cmd, rest=None): return self.ntransfercmd(cmd, rest)[0] def login(self, user = '', passwd = '', acct = ''): - '''Login, default anonymous.''' + """Login, default anonymous.""" if not user: user = 'anonymous' if not passwd: @@ -529,12 +536,12 @@ def storlines(self, cmd, fp, callback=None): return self.voidresp() def acct(self, password): - '''Send new account name.''' + """Send new account name.""" cmd = 'ACCT ' + password return self.voidcmd(cmd) def nlst(self, *args): - '''Return a list of files in a given directory (default the current).''' + """Return a list of files in a given directory (default the current).""" cmd = 'NLST' for arg in args: cmd = cmd + (' ' + arg) @@ -543,11 +550,12 @@ def nlst(self, *args): return files def dir(self, *args): - '''List a directory in long form. + """List a directory in long form. By default list current directory to stdout. Optional last argument is callback function; all non-empty arguments before it are concatenated to the - LIST command. (This *should* only be used for a pathname.)''' + LIST command. (This *should* only be used for a pathname.) + """ cmd = 'LIST' func = None if args[-1:] and type(args[-1]) != type(''): @@ -558,7 +566,7 @@ def dir(self, *args): self.retrlines(cmd, func) def mlsd(self, path="", facts=[]): - '''List a directory in a standardized format by using MLSD + """List a directory in a standardized format by using MLSD command (RFC-3659). If path is omitted the current directory is assumed. "facts" is a list of strings representing the type of information desired (e.g. ["type", "size", "perm"]). @@ -568,7 +576,7 @@ def mlsd(self, path="", facts=[]): First element is the file name, the second one is a dictionary including a variable number of "facts" depending on the server and whether "facts" argument has been provided. - ''' + """ if facts: self.sendcmd("OPTS MLST " + ";".join(facts) + ";") if path: @@ -586,14 +594,14 @@ def mlsd(self, path="", facts=[]): yield (name, entry) def rename(self, fromname, toname): - '''Rename a file.''' + """Rename a file.""" resp = self.sendcmd('RNFR ' + fromname) if resp[0] != '3': raise error_reply(resp) return self.voidcmd('RNTO ' + toname) def delete(self, filename): - '''Delete a file.''' + """Delete a file.""" resp = self.sendcmd('DELE ' + filename) if resp[:3] in {'250', '200'}: return resp @@ -601,7 +609,7 @@ def delete(self, filename): raise error_reply(resp) def cwd(self, dirname): - '''Change to a directory.''' + """Change to a directory.""" if dirname == '..': try: return self.voidcmd('CDUP') @@ -614,7 +622,7 @@ def cwd(self, dirname): return self.voidcmd(cmd) def size(self, filename): - '''Retrieve the size of a file.''' + """Retrieve the size of a file.""" # The SIZE command is defined in RFC-3659 resp = self.sendcmd('SIZE ' + filename) if resp[:3] == '213': @@ -622,7 +630,7 @@ def size(self, filename): return int(s) def mkd(self, dirname): - '''Make a directory, return its full pathname.''' + """Make a directory, return its full pathname.""" resp = self.voidcmd('MKD ' + dirname) # fix around non-compliant implementations such as IIS shipped # with Windows server 2003 @@ -631,11 +639,11 @@ def mkd(self, dirname): return parse257(resp) def rmd(self, dirname): - '''Remove a directory.''' + """Remove a directory.""" return self.voidcmd('RMD ' + dirname) def pwd(self): - '''Return current working directory.''' + """Return current working directory.""" resp = self.voidcmd('PWD') # fix around non-compliant implementations such as IIS shipped # with Windows server 2003 @@ -644,13 +652,13 @@ def pwd(self): return parse257(resp) def quit(self): - '''Quit, and close the connection.''' + """Quit, and close the connection.""" resp = self.voidcmd('QUIT') self.close() return resp def close(self): - '''Close the connection without assuming anything about it.''' + """Close the connection without assuming anything about it.""" try: file = self.file self.file = None @@ -670,7 +678,7 @@ def close(self): _SSLSocket = ssl.SSLSocket class FTP_TLS(FTP): - '''A FTP subclass which adds TLS support to FTP as described + """A FTP subclass which adds TLS support to FTP as described in RFC-4217. Connect as usual to port 21 implicitly securing the FTP control @@ -701,7 +709,7 @@ class FTP_TLS(FTP): >>> ftps.quit() '221 Goodbye.' >>> - ''' + """ ssl_version = ssl.PROTOCOL_TLS_CLIENT def __init__(self, host='', user='', passwd='', acct='', keyfile=None, @@ -733,7 +741,7 @@ def login(self, user='', passwd='', acct='', secure=True): return FTP.login(self, user, passwd, acct) def auth(self): - '''Set up secure control connection by using TLS/SSL.''' + """Set up secure control connection by using TLS/SSL.""" if isinstance(self.sock, ssl.SSLSocket): raise ValueError("Already using TLS") if self.ssl_version >= ssl.PROTOCOL_TLS: @@ -746,7 +754,7 @@ def auth(self): return resp def ccc(self): - '''Switch back to a clear-text control connection.''' + """Switch back to a clear-text control connection.""" if not isinstance(self.sock, ssl.SSLSocket): raise ValueError("not using TLS") resp = self.voidcmd('CCC') @@ -754,7 +762,7 @@ def ccc(self): return resp def prot_p(self): - '''Set up secure data connection.''' + """Set up secure data connection.""" # PROT defines whether or not the data channel is to be protected. # Though RFC-2228 defines four possible protection levels, # RFC-4217 only recommends two, Clear and Private. @@ -770,7 +778,7 @@ def prot_p(self): return resp def prot_c(self): - '''Set up clear text data connection.''' + """Set up clear text data connection.""" resp = self.voidcmd('PROT C') self._prot_p = False return resp @@ -800,10 +808,10 @@ def abort(self): _150_re = None def parse150(resp): - '''Parse the '150' response for a RETR request. + """Parse the '150' response for a RETR request. Returns the expected transfer size or None; size is not guaranteed to be present in the 150 message. - ''' + """ if resp[:3] != '150': raise error_reply(resp) global _150_re @@ -820,10 +828,10 @@ def parse150(resp): _227_re = None def parse227(resp): - '''Parse the '227' response for a PASV request. + """Parse the '227' response for a PASV request. Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)' - Return ('host.addr.as.numbers', port#) tuple.''' - + Return ('host.addr.as.numbers', port#) tuple. + """ if resp[:3] != '227': raise error_reply(resp) global _227_re @@ -840,10 +848,10 @@ def parse227(resp): def parse229(resp, peer): - '''Parse the '229' response for an EPSV request. + """Parse the '229' response for an EPSV request. Raises error_proto if it does not contain '(|||port|)' - Return ('host.addr.as.numbers', port#) tuple.''' - + Return ('host.addr.as.numbers', port#) tuple. + """ if resp[:3] != '229': raise error_reply(resp) left = resp.find('(') @@ -862,10 +870,10 @@ def parse229(resp, peer): def parse257(resp): - '''Parse the '257' response for a MKD or PWD request. + """Parse the '257' response for a MKD or PWD request. This is a response to a MKD or PWD request: a directory name. - Returns the directoryname in the 257 reply.''' - + Returns the directoryname in the 257 reply. + """ if resp[:3] != '257': raise error_reply(resp) if resp[3:5] != ' "': @@ -885,12 +893,12 @@ def parse257(resp): def print_line(line): - '''Default retrlines callback to print a line.''' + """Default retrlines callback to print a line.""" print(line) def ftpcp(source, sourcename, target, targetname = '', type = 'I'): - '''Copy file from one FTP-instance to another.''' + """Copy file from one FTP-instance to another.""" if not targetname: targetname = sourcename type = 'TYPE ' + type @@ -912,13 +920,13 @@ def ftpcp(source, sourcename, target, targetname = '', type = 'I'): def test(): - '''Test program. + """Test program. Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ... -d dir -l list -p password - ''' + """ if len(sys.argv) < 2: print(test.__doc__) From 737a260b98c6cd8435125a1f34922b539a442549 Mon Sep 17 00:00:00 2001 From: Alan Yee Date: Sat, 31 Aug 2019 09:54:04 -0700 Subject: [PATCH 2/5] Update according to feedback --- Lib/ftplib.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 070b5d2b758fa4..7d2dd29eecf561 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -120,8 +120,8 @@ def __init__(self, host='', user='', passwd='', acct='', def __enter__(self): return self + # Context management protocol: try to quit() if active def __exit__(self, *args): - """Context management protocol: try to quit() if active""" if self.sock is not None: try: self.quit() @@ -180,15 +180,15 @@ def set_pasv(self, val): """ self.passiveserver = val + # Internal: 'sanitize' a string for printing def sanitize(self, s): - """Internal: 'sanitize' a string for printing""" if s[:5] in {'pass ', 'PASS '}: i = len(s.rstrip('\r\n')) s = s[:5] + '*'*(i-5) + s[i:] return repr(s) + # Internal: send one line to the server, appending CRLF def putline(self, line): - """Internal: send one line to the server, appending CRLF""" if '\r' in line or '\n' in line: raise ValueError('an illegal newline character should not be contained') sys.audit("ftplib.sendcmd", self, line) @@ -197,15 +197,14 @@ def putline(self, line): print('*put*', self.sanitize(line)) self.sock.sendall(line.encode(self.encoding)) + # Internal: send one command to the server (through putline()) def putcmd(self, line): - """Internal: send one command to the server (through putline())""" if self.debugging: print('*cmd*', self.sanitize(line)) self.putline(line) + # Internal: return one line from the server, stripping CRLF. + # Raise EOFError if the connection is closed def getline(self): - """Internal: return one line from the server, stripping CRLF. - Raise EOFError if the connection is closed - """ line = self.file.readline(self.maxline + 1) if len(line) > self.maxline: raise Error("got more than %d bytes" % self.maxline) @@ -219,13 +218,11 @@ def getline(self): line = line[:-1] return line - # + # Internal: get a response from the server, which may possibly + # consist of multiple lines. Return a single string with no + # trailing CRLF. If the response consists of multiple lines, + # these are separated by '\n' characters in the string def getmultiline(self): - """Internal: get a response from the server, which may possibly - consist of multiple lines. Return a single string with no - trailing CRLF. If the response consists of multiple lines, - these are separated by '\n' characters in the string - """ line = self.getline() if line[3:4] == '-': code = line[:3] @@ -237,10 +234,9 @@ def getmultiline(self): break return line + # Internal: get a response from the server. + # Raise various errors if the response indicates an error def getresp(self): - """Internal: get a response from the server. - Raise various errors if the response indicates an error - """ resp = self.getmultiline() if self.debugging: print('*resp*', self.sanitize(resp)) From d97616c49a55457efb81b79bb3c116c157d96403 Mon Sep 17 00:00:00 2001 From: Alan Yee Date: Sun, 1 Sep 2019 10:28:03 -0700 Subject: [PATCH 3/5] Revert exclusive format changes --- Lib/ftplib.py | 113 +++++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 60 deletions(-) diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 7d2dd29eecf561..410d6f9fd5d524 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -72,7 +72,7 @@ class error_proto(Error): pass # response does not begin with [1-5] # The class itself class FTP: - """An FTP client class. + '''An FTP client class. To create a connection, call the class using these arguments: host, user, passwd, acct, timeout @@ -92,7 +92,8 @@ class FTP: below for details). The download/upload functions first issue appropriate TYPE and PORT or PASV commands. - """ + ''' + debugging = 0 host = '' port = FTP_PORT @@ -132,13 +133,13 @@ def __exit__(self, *args): self.close() def connect(self, host='', port=0, timeout=-999, source_address=None): - """Connect to host. Arguments are: + '''Connect to host. Arguments are: - host: hostname to connect to (string, default previous host) - port: port to connect to (integer, default previous port) - timeout: the timeout to set against the ftp socket(s) - source_address: a 2-tuple (host, port) for the socket to bind to as its source address before connecting. - """ + ''' if host != '': self.host = host if port > 0: @@ -156,31 +157,28 @@ def connect(self, host='', port=0, timeout=-999, source_address=None): return self.welcome def getwelcome(self): - """Get the welcome message from the server. - (this is read and squirreled away by connect()) - """ + '''Get the welcome message from the server. + (this is read and squirreled away by connect())''' if self.debugging: print('*welcome*', self.sanitize(self.welcome)) return self.welcome def set_debuglevel(self, level): - """Set the debugging level. + '''Set the debugging level. The required argument level means: 0: no debugging output (default) 1: print commands and responses but not body text etc. - 2: also print raw lines read and sent before stripping CR/LF - """ + 2: also print raw lines read and sent before stripping CR/LF''' self.debugging = level debug = set_debuglevel def set_pasv(self, val): - """Use passive or active mode for data transfers. + '''Use passive or active mode for data transfers. With a false argument, use the normal PORT mode, - With a true argument, use the PASV command. - """ + With a true argument, use the PASV command.''' self.passiveserver = val - # Internal: 'sanitize' a string for printing + # Internal: "sanitize" a string for printing def sanitize(self, s): if s[:5] in {'pass ', 'PASS '}: i = len(s.rstrip('\r\n')) @@ -258,11 +256,10 @@ def voidresp(self): return resp def abort(self): - """Abort a file transfer. Uses out-of-band data. + '''Abort a file transfer. Uses out-of-band data. This does not follow the procedure from the RFC to send Telnet IP and Synch; that doesn't seem to work with the servers I've - tried. Instead, just send the ABOR command as OOB data. - """ + tried. Instead, just send the ABOR command as OOB data.''' line = b'ABOR' + B_CRLF if self.debugging > 1: print('*put urgent*', self.sanitize(line)) @@ -273,7 +270,7 @@ def abort(self): return resp def sendcmd(self, cmd): - """Send a command and return the response.""" + '''Send a command and return the response.''' self.putcmd(cmd) return self.getresp() @@ -283,9 +280,9 @@ def voidcmd(self, cmd): return self.voidresp() def sendport(self, host, port): - """Send a PORT command with the current host and the given + '''Send a PORT command with the current host and the given port number. - """ + ''' hbytes = host.split('.') pbytes = [repr(port//256), repr(port%256)] bytes = hbytes + pbytes @@ -293,7 +290,7 @@ def sendport(self, host, port): return self.voidcmd(cmd) def sendeprt(self, host, port): - """Send an EPRT command with the current host and the given port number.""" + '''Send an EPRT command with the current host and the given port number.''' af = 0 if self.af == socket.AF_INET: af = 1 @@ -306,7 +303,7 @@ def sendeprt(self, host, port): return self.voidcmd(cmd) def makeport(self): - """Create a new socket and send a PORT command for it.""" + '''Create a new socket and send a PORT command for it.''' sock = socket.create_server(("", 0), family=self.af, backlog=1) port = sock.getsockname()[1] # Get proper port host = self.sock.getsockname()[0] # Get proper host @@ -385,7 +382,7 @@ def transfercmd(self, cmd, rest=None): return self.ntransfercmd(cmd, rest)[0] def login(self, user = '', passwd = '', acct = ''): - """Login, default anonymous.""" + '''Login, default anonymous.''' if not user: user = 'anonymous' if not passwd: @@ -532,12 +529,12 @@ def storlines(self, cmd, fp, callback=None): return self.voidresp() def acct(self, password): - """Send new account name.""" + '''Send new account name.''' cmd = 'ACCT ' + password return self.voidcmd(cmd) def nlst(self, *args): - """Return a list of files in a given directory (default the current).""" + '''Return a list of files in a given directory (default the current).''' cmd = 'NLST' for arg in args: cmd = cmd + (' ' + arg) @@ -546,12 +543,11 @@ def nlst(self, *args): return files def dir(self, *args): - """List a directory in long form. + '''List a directory in long form. By default list current directory to stdout. Optional last argument is callback function; all non-empty arguments before it are concatenated to the - LIST command. (This *should* only be used for a pathname.) - """ + LIST command. (This *should* only be used for a pathname.)''' cmd = 'LIST' func = None if args[-1:] and type(args[-1]) != type(''): @@ -562,7 +558,7 @@ def dir(self, *args): self.retrlines(cmd, func) def mlsd(self, path="", facts=[]): - """List a directory in a standardized format by using MLSD + '''List a directory in a standardized format by using MLSD command (RFC-3659). If path is omitted the current directory is assumed. "facts" is a list of strings representing the type of information desired (e.g. ["type", "size", "perm"]). @@ -572,7 +568,7 @@ def mlsd(self, path="", facts=[]): First element is the file name, the second one is a dictionary including a variable number of "facts" depending on the server and whether "facts" argument has been provided. - """ + ''' if facts: self.sendcmd("OPTS MLST " + ";".join(facts) + ";") if path: @@ -590,14 +586,14 @@ def mlsd(self, path="", facts=[]): yield (name, entry) def rename(self, fromname, toname): - """Rename a file.""" + '''Rename a file.''' resp = self.sendcmd('RNFR ' + fromname) if resp[0] != '3': raise error_reply(resp) return self.voidcmd('RNTO ' + toname) def delete(self, filename): - """Delete a file.""" + '''Delete a file.''' resp = self.sendcmd('DELE ' + filename) if resp[:3] in {'250', '200'}: return resp @@ -605,7 +601,7 @@ def delete(self, filename): raise error_reply(resp) def cwd(self, dirname): - """Change to a directory.""" + '''Change to a directory.''' if dirname == '..': try: return self.voidcmd('CDUP') @@ -618,7 +614,7 @@ def cwd(self, dirname): return self.voidcmd(cmd) def size(self, filename): - """Retrieve the size of a file.""" + '''Retrieve the size of a file.''' # The SIZE command is defined in RFC-3659 resp = self.sendcmd('SIZE ' + filename) if resp[:3] == '213': @@ -626,7 +622,7 @@ def size(self, filename): return int(s) def mkd(self, dirname): - """Make a directory, return its full pathname.""" + '''Make a directory, return its full pathname.''' resp = self.voidcmd('MKD ' + dirname) # fix around non-compliant implementations such as IIS shipped # with Windows server 2003 @@ -635,11 +631,11 @@ def mkd(self, dirname): return parse257(resp) def rmd(self, dirname): - """Remove a directory.""" + '''Remove a directory.''' return self.voidcmd('RMD ' + dirname) def pwd(self): - """Return current working directory.""" + '''Return current working directory.''' resp = self.voidcmd('PWD') # fix around non-compliant implementations such as IIS shipped # with Windows server 2003 @@ -648,13 +644,13 @@ def pwd(self): return parse257(resp) def quit(self): - """Quit, and close the connection.""" + '''Quit, and close the connection.''' resp = self.voidcmd('QUIT') self.close() return resp def close(self): - """Close the connection without assuming anything about it.""" + '''Close the connection without assuming anything about it.''' try: file = self.file self.file = None @@ -674,7 +670,7 @@ def close(self): _SSLSocket = ssl.SSLSocket class FTP_TLS(FTP): - """A FTP subclass which adds TLS support to FTP as described + '''A FTP subclass which adds TLS support to FTP as described in RFC-4217. Connect as usual to port 21 implicitly securing the FTP control @@ -705,7 +701,7 @@ class FTP_TLS(FTP): >>> ftps.quit() '221 Goodbye.' >>> - """ + ''' ssl_version = ssl.PROTOCOL_TLS_CLIENT def __init__(self, host='', user='', passwd='', acct='', keyfile=None, @@ -737,7 +733,7 @@ def login(self, user='', passwd='', acct='', secure=True): return FTP.login(self, user, passwd, acct) def auth(self): - """Set up secure control connection by using TLS/SSL.""" + '''Set up secure control connection by using TLS/SSL.''' if isinstance(self.sock, ssl.SSLSocket): raise ValueError("Already using TLS") if self.ssl_version >= ssl.PROTOCOL_TLS: @@ -750,7 +746,7 @@ def auth(self): return resp def ccc(self): - """Switch back to a clear-text control connection.""" + '''Switch back to a clear-text control connection.''' if not isinstance(self.sock, ssl.SSLSocket): raise ValueError("not using TLS") resp = self.voidcmd('CCC') @@ -758,7 +754,7 @@ def ccc(self): return resp def prot_p(self): - """Set up secure data connection.""" + '''Set up secure data connection.''' # PROT defines whether or not the data channel is to be protected. # Though RFC-2228 defines four possible protection levels, # RFC-4217 only recommends two, Clear and Private. @@ -774,7 +770,7 @@ def prot_p(self): return resp def prot_c(self): - """Set up clear text data connection.""" + '''Set up clear text data connection.''' resp = self.voidcmd('PROT C') self._prot_p = False return resp @@ -804,10 +800,10 @@ def abort(self): _150_re = None def parse150(resp): - """Parse the '150' response for a RETR request. + '''Parse the '150' response for a RETR request. Returns the expected transfer size or None; size is not guaranteed to be present in the 150 message. - """ + ''' if resp[:3] != '150': raise error_reply(resp) global _150_re @@ -824,10 +820,9 @@ def parse150(resp): _227_re = None def parse227(resp): - """Parse the '227' response for a PASV request. + '''Parse the '227' response for a PASV request. Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)' - Return ('host.addr.as.numbers', port#) tuple. - """ + Return ('host.addr.as.numbers', port#) tuple.''' if resp[:3] != '227': raise error_reply(resp) global _227_re @@ -844,10 +839,9 @@ def parse227(resp): def parse229(resp, peer): - """Parse the '229' response for an EPSV request. + '''Parse the '229' response for an EPSV request. Raises error_proto if it does not contain '(|||port|)' - Return ('host.addr.as.numbers', port#) tuple. - """ + Return ('host.addr.as.numbers', port#) tuple.''' if resp[:3] != '229': raise error_reply(resp) left = resp.find('(') @@ -866,10 +860,9 @@ def parse229(resp, peer): def parse257(resp): - """Parse the '257' response for a MKD or PWD request. + '''Parse the '257' response for a MKD or PWD request. This is a response to a MKD or PWD request: a directory name. - Returns the directoryname in the 257 reply. - """ + Returns the directoryname in the 257 reply.''' if resp[:3] != '257': raise error_reply(resp) if resp[3:5] != ' "': @@ -889,12 +882,12 @@ def parse257(resp): def print_line(line): - """Default retrlines callback to print a line.""" + '''Default retrlines callback to print a line.''' print(line) def ftpcp(source, sourcename, target, targetname = '', type = 'I'): - """Copy file from one FTP-instance to another.""" + '''Copy file from one FTP-instance to another.''' if not targetname: targetname = sourcename type = 'TYPE ' + type @@ -916,13 +909,13 @@ def ftpcp(source, sourcename, target, targetname = '', type = 'I'): def test(): - """Test program. + '''Test program. Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ... -d dir -l list -p password - """ + ''' if len(sys.argv) < 2: print(test.__doc__) From fee51e06b4560668a2f554d17c926f82bed503ea Mon Sep 17 00:00:00 2001 From: Alan Yee Date: Mon, 2 Sep 2019 12:38:06 -0700 Subject: [PATCH 4/5] Update Lib/ftplib.py Co-Authored-By: Kyle Stanley --- Lib/ftplib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 410d6f9fd5d524..31c07031666f3b 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -107,7 +107,7 @@ class FTP: def __init__(self, host='', user='', passwd='', acct='', timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None): """Initialization method (called by class instantiation). - Initialize host to localhost, port to standard ftp port + Initialize host to localhost, port to standard ftp port. Optional arguments are host (for connect()), and user, passwd, acct (for login()) """ From d71f89d253b78409ecb4118c48f9a4a388417515 Mon Sep 17 00:00:00 2001 From: Alan Yee Date: Mon, 2 Sep 2019 12:38:14 -0700 Subject: [PATCH 5/5] Update Lib/ftplib.py Co-Authored-By: Kyle Stanley --- Lib/ftplib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 31c07031666f3b..c339eb25bc2d78 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -109,7 +109,7 @@ def __init__(self, host='', user='', passwd='', acct='', """Initialization method (called by class instantiation). Initialize host to localhost, port to standard ftp port. Optional arguments are host (for connect()), - and user, passwd, acct (for login()) + and user, passwd, acct (for login()). """ self.source_address = source_address self.timeout = timeout