diff options
author | Tim Peters <tim.peters@gmail.com> | 2001-01-14 23:36:06 (GMT) |
---|---|---|
committer | Tim Peters <tim.peters@gmail.com> | 2001-01-14 23:36:06 (GMT) |
commit | 88869f9787cd4ceb2298e4b13980beb057687824 (patch) | |
tree | 653976731f926b5e098bf57b4858a9516af220b5 /Lib | |
parent | 752d3f557e95cda98a8b50808cfe2f2d0b8767eb (diff) | |
download | cpython-88869f9787cd4ceb2298e4b13980beb057687824.zip cpython-88869f9787cd4ceb2298e4b13980beb057687824.tar.gz cpython-88869f9787cd4ceb2298e4b13980beb057687824.tar.bz2 |
Whitespace normalization.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/ConfigParser.py | 16 | ||||
-rw-r--r-- | Lib/Cookie.py | 28 | ||||
-rw-r--r-- | Lib/calendar.py | 6 | ||||
-rwxr-xr-x | Lib/cgi.py | 54 | ||||
-rw-r--r-- | Lib/chunk.py | 15 | ||||
-rw-r--r-- | Lib/codecs.py | 14 | ||||
-rw-r--r-- | Lib/commands.py | 6 | ||||
-rw-r--r-- | Lib/copy.py | 366 | ||||
-rw-r--r-- | Lib/dis.py | 328 | ||||
-rw-r--r-- | Lib/dospath.py | 4 | ||||
-rw-r--r-- | Lib/dumbdbm.py | 214 | ||||
-rw-r--r-- | Lib/filecmp.py | 6 | ||||
-rw-r--r-- | Lib/fnmatch.py | 136 | ||||
-rw-r--r-- | Lib/formatter.py | 4 | ||||
-rw-r--r-- | Lib/fpformat.py | 1 | ||||
-rw-r--r-- | Lib/ftplib.py | 1270 |
16 files changed, 1233 insertions, 1235 deletions
diff --git a/Lib/ConfigParser.py b/Lib/ConfigParser.py index 2f60742..f1e77e8 100644 --- a/Lib/ConfigParser.py +++ b/Lib/ConfigParser.py @@ -73,16 +73,16 @@ ConfigParser -- responsible for for parsing a list of 1, only) remove_section(section) - remove the given file section and all its options + remove the given file section and all its options remove_option(section, option) - remove the given option from the given section + remove the given option from the given section set(section, option, value) set the given option write(fp) - write the configuration state in .ini format + write the configuration state in .ini format """ import sys @@ -94,7 +94,7 @@ DEFAULTSECT = "DEFAULT" MAX_INTERPOLATION_DEPTH = 10 - + # exception classes class Error(Exception): def __init__(self, msg=''): @@ -166,7 +166,7 @@ class MissingSectionHeaderError(ParsingError): self.line = line - + class ConfigParser: def __init__(self, defaults=None): self.__sections = {} @@ -217,7 +217,7 @@ class ConfigParser: def read(self, filenames): """Read and parse a filename or a list of filenames. - + Files that cannot be opened are silently ignored; this is designed so that you can specify a list of potential configuration file locations (e.g. current directory, user's @@ -285,7 +285,7 @@ class ConfigParser: # do the string interpolation value = rawval # Make it a pretty variable name - depth = 0 + depth = 0 while depth < 10: # Loop through this until it's done depth = depth + 1 if string.find(value, "%(") >= 0: @@ -298,7 +298,7 @@ class ConfigParser: if value.find("%(") >= 0: raise InterpolationDepthError(option, section, rawval) return value - + def __get(self, section, conv, option): return conv(self.get(section, option)) diff --git a/Lib/Cookie.py b/Lib/Cookie.py index 223e715..4ff0cbb 100644 --- a/Lib/Cookie.py +++ b/Lib/Cookie.py @@ -3,9 +3,9 @@ #### # Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu> -# +# # All Rights Reserved -# +# # Permission to use, copy, modify, and distribute this software # and its documentation for any purpose and without fee is hereby # granted, provided that the above copyright notice appear in all @@ -13,8 +13,8 @@ # notice appear in supporting documentation, and that the name of # Timothy O'Malley not be used in advertising or publicity # pertaining to distribution of the software without specific, written -# prior permission. -# +# prior permission. +# # Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR @@ -22,11 +22,11 @@ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. +# PERFORMANCE OF THIS SOFTWARE. # #### -# -# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp +# +# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp # by Timothy O'Malley <timo@alum.mit.edu> # # Cookie.py is a Python module for the handling of HTTP @@ -116,7 +116,7 @@ attribute. Set-Cookie: oreo="doublestuff"; Path=/; Each dictionary element has a 'value' attribute, which gives you -back the value associated with the key. +back the value associated with the key. >>> C = Cookie.SmartCookie() >>> C["twix"] = "none for you" @@ -148,7 +148,7 @@ the value to a string, when the values are set dictionary-style. Set-Cookie: number=7; Set-Cookie: string=seven; - + SerialCookie The SerialCookie expects that all values should be serialized using @@ -214,7 +214,7 @@ Finis. # # Import our required modules -# +# import string, sys from UserDict import UserDict @@ -242,7 +242,7 @@ class CookieError(Exception): # into a 4 character sequence: a forward-slash followed by the # three-digit octal equivalent of the character. Any '\' or '"' is # quoted with a preceeding '\' slash. -# +# # These are taken from RFC2068 and RFC2109. # _LegalChars is the list of chars which don't require "'s # _Translator hash-table for fast quoting @@ -319,7 +319,7 @@ def _quote(str, LegalChars=_LegalChars, if "" == translate(str, idmap, LegalChars): return str else: - return '"' + join( map(_Translator.get, str, str), "" ) + '"' + return '"' + join( map(_Translator.get, str, str), "" ) + '"' # end _quote @@ -370,7 +370,7 @@ def _unquote(str, join=string.join, atoi=string.atoi): # The _getdate() routine is used to set the expiration time in # the cookie's HTTP header. By default, _getdate() returns the -# current time in the appropriate "expires" format for a +# current time in the appropriate "expires" format for a # Set-Cookie header. The one optional argument is an offset from # now, in seconds. For example, an offset of -3600 means "one hour ago". # The offset may be a floating point number. @@ -405,7 +405,7 @@ class Morsel(UserDict): # RFC 2109 lists these attributes as reserved: # path comment domain # max-age secure version - # + # # For historical reasons, these attributes are also reserved: # expires # diff --git a/Lib/calendar.py b/Lib/calendar.py index 01c2194..1d15081 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -131,7 +131,7 @@ def month(theyear, themonth, w=0, l=0): """Return a month's calendar string (multi-line).""" w = max(2, w) l = max(1, l) - s = (_center(month_name[themonth] + ' ' + `theyear`, + s = (_center(month_name[themonth] + ' ' + `theyear`, 7 * (w + 1) - 1).rstrip() + '\n' * l + weekheader(w).rstrip() + '\n' * l) for aweek in monthcalendar(theyear, themonth): @@ -167,7 +167,7 @@ def calendar(year, w=0, l=0, c=_spacing): for q in range(January, January+12, 3): s = (s + '\n' * l + format3cstring(month_name[q], month_name[q+1], month_name[q+2], - colwidth, c).rstrip() + + colwidth, c).rstrip() + '\n' * l + header + '\n' * l) data = [] height = 0 @@ -183,7 +183,7 @@ def calendar(year, w=0, l=0, c=_spacing): weeks.append('') else: weeks.append(week(cal[i], w)) - s = s + format3cstring(weeks[0], weeks[1], weeks[2], + s = s + format3cstring(weeks[0], weeks[1], weeks[2], colwidth, c).rstrip() + '\n' * l return s[:-l] + '\n' @@ -11,13 +11,13 @@ written in Python. # History # ------- -# +# # Michael McLay started this module. Steve Majewski changed the # interface to SvFormContentDict and FormContentDict. The multipart # parsing was inspired by code submitted by Andreas Paepcke. Guido van # Rossum rewrote, reformatted and documented the module and is currently # responsible for its maintenance. -# +# __version__ = "2.5" @@ -104,8 +104,8 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): environ : environment dictionary; default: os.environ keep_blank_values: flag indicating whether blank values in - URL encoded forms should be treated as blank strings. - A true value indicates that blanks should be retained as + URL encoded forms should be treated as blank strings. + A true value indicates that blanks should be retained as blank strings. The default false value indicates that blank values are to be ignored and treated as if they were not included. @@ -129,10 +129,10 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): qs = fp.read(clength) else: qs = '' # Unknown content-type - if environ.has_key('QUERY_STRING'): + if environ.has_key('QUERY_STRING'): if qs: qs = qs + '&' qs = qs + environ['QUERY_STRING'] - elif sys.argv[1:]: + elif sys.argv[1:]: if qs: qs = qs + '&' qs = qs + sys.argv[1] environ['QUERY_STRING'] = qs # XXX Shouldn't, really @@ -155,8 +155,8 @@ def parse_qs(qs, keep_blank_values=0, strict_parsing=0): qs: URL-encoded query string to be parsed keep_blank_values: flag indicating whether blank values in - URL encoded queries should be treated as blank strings. - A true value indicates that blanks should be retained as + URL encoded queries should be treated as blank strings. + A true value indicates that blanks should be retained as blank strings. The default false value indicates that blank values are to be ignored and treated as if they were not included. @@ -188,7 +188,7 @@ def parse_qsl(qs, keep_blank_values=0, strict_parsing=0): strict_parsing: flag indicating what to do with parsing errors. If false (the default), errors are silently ignored. If true, - errors raise a ValueError exception. + errors raise a ValueError exception. Returns a list, as G-d intended. """ @@ -215,17 +215,17 @@ def parse_multipart(fp, pdict): fp : input file pdict: dictionary containing other parameters of conten-type header - Returns a dictionary just like parse_qs(): keys are the field names, each - value is a list of values for that field. This is easy to use but not - much good if you are expecting megabytes to be uploaded -- in that case, - use the FieldStorage class instead which is much more flexible. Note - that content-type is the raw, unparsed contents of the content-type + Returns a dictionary just like parse_qs(): keys are the field names, each + value is a list of values for that field. This is easy to use but not + much good if you are expecting megabytes to be uploaded -- in that case, + use the FieldStorage class instead which is much more flexible. Note + that content-type is the raw, unparsed contents of the content-type header. - - XXX This does not parse nested multipart parts -- use FieldStorage for + + XXX This does not parse nested multipart parts -- use FieldStorage for that. - - XXX This should really be subsumed by FieldStorage altogether -- no + + XXX This should really be subsumed by FieldStorage altogether -- no point in having two implementations of the same parsing algorithm. """ @@ -409,8 +409,8 @@ class FieldStorage: environ : environment dictionary; default: os.environ keep_blank_values: flag indicating whether blank values in - URL encoded forms should be treated as blank strings. - A true value indicates that blanks should be retained as + URL encoded forms should be treated as blank strings. + A true value indicates that blanks should be retained as blank strings. The default false value indicates that blank values are to be ignored and treated as if they were not included. @@ -707,7 +707,7 @@ class FieldStorage: """ import tempfile return tempfile.TemporaryFile("w+b") - + # Backwards Compatibility Classes @@ -744,8 +744,8 @@ class SvFormContentDict(FormContentDict): """ def __getitem__(self, key): - if len(self.dict[key]) > 1: - raise IndexError, 'expecting a single value' + if len(self.dict[key]) > 1: + raise IndexError, 'expecting a single value' return self.dict[key][0] def getlist(self, key): return self.dict[key] @@ -766,7 +766,7 @@ class SvFormContentDict(FormContentDict): class InterpFormContentDict(SvFormContentDict): - """This class is present for backwards compatibility only.""" + """This class is present for backwards compatibility only.""" def __getitem__(self, key): v = SvFormContentDict.__getitem__(self, key) if v[0] in string.digits + '+-.': @@ -794,7 +794,7 @@ class InterpFormContentDict(SvFormContentDict): class FormContent(FormContentDict): - """This class is present for backwards compatibility only.""" + """This class is present for backwards compatibility only.""" def values(self, key): if self.dict.has_key(key) :return self.dict[key] else: return None @@ -882,7 +882,7 @@ def print_environ(environ=os.environ): print "<DL>" for key in keys: print "<DT>", escape(key), "<DD>", escape(environ[key]) - print "</DL>" + print "</DL>" print def print_form(form): @@ -982,5 +982,5 @@ def escape(s, quote=None): # =============== # Call test() when this file is run as a script (not imported as a module) -if __name__ == '__main__': +if __name__ == '__main__': test() diff --git a/Lib/chunk.py b/Lib/chunk.py index fbbb1c1..0a93cd3 100644 --- a/Lib/chunk.py +++ b/Lib/chunk.py @@ -18,7 +18,7 @@ The size field (a 32-bit value, encoded using big-endian byte order) gives the size of the whole chunk, including the 8-byte header. Usually an IFF-type file consists of one or more chunks. The proposed -usage of the Chunk class defined here is to instantiate an instance at +usage of the Chunk class defined here is to instantiate an instance at the start of each chunk and read from the instance until it reaches the end, after which a new instance can be instantiated. At the end of the file, creating a new instance will fail with a EOFError @@ -44,7 +44,7 @@ getname() (returns the name (ID) of the chunk) The __init__ method has one required argument, a file-like object (including a chunk instance), and one optional argument, a flag which -specifies whether or not chunks are aligned on 2-byte boundaries. The +specifies whether or not chunks are aligned on 2-byte boundaries. The default is 1, i.e. aligned. """ @@ -52,7 +52,7 @@ class Chunk: def __init__(self, file, align = 1, bigendian = 1, inclheader = 0): import struct self.closed = 0 - self.align = align # whether to align to word (2-byte) boundaries + self.align = align # whether to align to word (2-byte) boundaries if bigendian: strflag = '>' else: @@ -97,7 +97,7 @@ class Chunk: """Seek to specified position into the chunk. Default position is 0 (start of chunk). If the file is not seekable, this will result in an error. - """ + """ if self.closed: raise ValueError, "I/O operation on closed file" @@ -121,7 +121,7 @@ class Chunk: """Read at most size bytes from the chunk. If size is omitted or negative, read until the end of the chunk. - """ + """ if self.closed: raise ValueError, "I/O operation on closed file" @@ -130,7 +130,7 @@ class Chunk: if size < 0: size = self.chunksize - self.size_read if size > self.chunksize - self.size_read: - size = self.chunksize - self.size_read + size = self.chunksize - self.size_read data = self.file.read(size) self.size_read = self.size_read + len(data) if self.size_read == self.chunksize and \ @@ -145,7 +145,7 @@ class Chunk: If you are not interested in the contents of the chunk, this method should be called so that the file points to the start of the next chunk. - """ + """ if self.closed: raise ValueError, "I/O operation on closed file" @@ -165,4 +165,3 @@ class Chunk: dummy = self.read(n) if not dummy: raise EOFError - diff --git a/Lib/codecs.py b/Lib/codecs.py index 9931137..003aa01 100644 --- a/Lib/codecs.py +++ b/Lib/codecs.py @@ -25,19 +25,19 @@ except ImportError,why: BOM = struct.pack('=H',0xFEFF) # BOM_BE = BOM32_BE = '\376\377' -# corresponds to Unicode U+FEFF in UTF-16 on big endian -# platforms == ZERO WIDTH NO-BREAK SPACE +# corresponds to Unicode U+FEFF in UTF-16 on big endian +# platforms == ZERO WIDTH NO-BREAK SPACE BOM_LE = BOM32_LE = '\377\376' -# corresponds to Unicode U+FFFE in UTF-16 on little endian -# platforms == defined as being an illegal Unicode character +# corresponds to Unicode U+FFFE in UTF-16 on little endian +# platforms == defined as being an illegal Unicode character # # 64-bit Byte Order Marks # BOM64_BE = '\000\000\376\377' -# corresponds to Unicode U+0000FEFF in UCS-4 +# corresponds to Unicode U+0000FEFF in UCS-4 BOM64_LE = '\377\376\000\000' -# corresponds to Unicode U+0000FFFE in UCS-4 +# corresponds to Unicode U+0000FFFE in UCS-4 ### Codec base classes (defining the API) @@ -547,7 +547,7 @@ def make_identity_dict(rng): Return a dictionary where elements of the rng sequence are mapped to themselves. - + """ res = {} for i in rng: diff --git a/Lib/commands.py b/Lib/commands.py index 99387b1..a21460d 100644 --- a/Lib/commands.py +++ b/Lib/commands.py @@ -1,9 +1,9 @@ """Execute shell commands via os.popen() and return status, output. Interface summary: - + import commands - + outtext = commands.getoutput(cmd) (exitstatus, outtext) = commands.getstatusoutput(cmd) outtext = commands.getstatus(file) # returns output of "ls -ld file" @@ -11,7 +11,7 @@ Interface summary: A trailing newline is removed from the output string. Encapsulates the basic operation: - + pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r') text = pipe.read() sts = pipe.close() diff --git a/Lib/copy.py b/Lib/copy.py index fc2e1ff..cdd2fdf 100644 --- a/Lib/copy.py +++ b/Lib/copy.py @@ -2,10 +2,10 @@ Interface summary: - import copy + import copy - x = copy.copy(y) # make a shallow copy of y - x = copy.deepcopy(y) # make a deep copy of y + x = copy.copy(y) # make a shallow copy of y + x = copy.deepcopy(y) # make a deep copy of y For module specific errors, copy.error is raised. @@ -53,8 +53,8 @@ __getstate__() and __setstate__(). See the documentation for module import types class Error(Exception): - pass -error = Error # backward compatibility + pass +error = Error # backward compatibility try: from org.python.core import PyStringMap @@ -62,28 +62,28 @@ except ImportError: PyStringMap = None def copy(x): - """Shallow copy operation on arbitrary Python objects. - - See the module's __doc__ string for more info. - """ - - try: - copierfunction = _copy_dispatch[type(x)] - except KeyError: - try: - copier = x.__copy__ - except AttributeError: - raise error, \ - "un(shallow)copyable object of type %s" % type(x) - y = copier() - else: - y = copierfunction(x) - return y + """Shallow copy operation on arbitrary Python objects. + + See the module's __doc__ string for more info. + """ + + try: + copierfunction = _copy_dispatch[type(x)] + except KeyError: + try: + copier = x.__copy__ + except AttributeError: + raise error, \ + "un(shallow)copyable object of type %s" % type(x) + y = copier() + else: + y = copierfunction(x) + return y _copy_dispatch = d = {} def _copy_atomic(x): - return x + return x d[types.NoneType] = _copy_atomic d[types.IntType] = _copy_atomic d[types.LongType] = _copy_atomic @@ -91,78 +91,78 @@ d[types.FloatType] = _copy_atomic d[types.StringType] = _copy_atomic d[types.UnicodeType] = _copy_atomic try: - d[types.CodeType] = _copy_atomic + d[types.CodeType] = _copy_atomic except AttributeError: - pass + pass d[types.TypeType] = _copy_atomic d[types.XRangeType] = _copy_atomic d[types.ClassType] = _copy_atomic def _copy_list(x): - return x[:] + return x[:] d[types.ListType] = _copy_list def _copy_tuple(x): - return x[:] + return x[:] d[types.TupleType] = _copy_tuple def _copy_dict(x): - return x.copy() + return x.copy() d[types.DictionaryType] = _copy_dict if PyStringMap is not None: d[PyStringMap] = _copy_dict def _copy_inst(x): - if hasattr(x, '__copy__'): - return x.__copy__() - if hasattr(x, '__getinitargs__'): - args = x.__getinitargs__() - y = apply(x.__class__, args) - else: - y = _EmptyClass() - y.__class__ = x.__class__ - if hasattr(x, '__getstate__'): - state = x.__getstate__() - else: - state = x.__dict__ - if hasattr(y, '__setstate__'): - y.__setstate__(state) - else: - y.__dict__.update(state) - return y + if hasattr(x, '__copy__'): + return x.__copy__() + if hasattr(x, '__getinitargs__'): + args = x.__getinitargs__() + y = apply(x.__class__, args) + else: + y = _EmptyClass() + y.__class__ = x.__class__ + if hasattr(x, '__getstate__'): + state = x.__getstate__() + else: + state = x.__dict__ + if hasattr(y, '__setstate__'): + y.__setstate__(state) + else: + y.__dict__.update(state) + return y d[types.InstanceType] = _copy_inst del d def deepcopy(x, memo = None): - """Deep copy operation on arbitrary Python objects. - - See the module's __doc__ string for more info. - """ - - if memo is None: - memo = {} - d = id(x) - if memo.has_key(d): - return memo[d] - try: - copierfunction = _deepcopy_dispatch[type(x)] - except KeyError: - try: - copier = x.__deepcopy__ - except AttributeError: - raise error, \ - "un-deep-copyable object of type %s" % type(x) - y = copier(memo) - else: - y = copierfunction(x, memo) - memo[d] = y - return y + """Deep copy operation on arbitrary Python objects. + + See the module's __doc__ string for more info. + """ + + if memo is None: + memo = {} + d = id(x) + if memo.has_key(d): + return memo[d] + try: + copierfunction = _deepcopy_dispatch[type(x)] + except KeyError: + try: + copier = x.__deepcopy__ + except AttributeError: + raise error, \ + "un-deep-copyable object of type %s" % type(x) + y = copier(memo) + else: + y = copierfunction(x, memo) + memo[d] = y + return y _deepcopy_dispatch = d = {} def _deepcopy_atomic(x, memo): - return x + return x d[types.NoneType] = _deepcopy_atomic d[types.IntType] = _deepcopy_atomic d[types.LongType] = _deepcopy_atomic @@ -174,81 +174,81 @@ d[types.TypeType] = _deepcopy_atomic d[types.XRangeType] = _deepcopy_atomic def _deepcopy_list(x, memo): - y = [] - memo[id(x)] = y - for a in x: - y.append(deepcopy(a, memo)) - return y + y = [] + memo[id(x)] = y + for a in x: + y.append(deepcopy(a, memo)) + return y d[types.ListType] = _deepcopy_list def _deepcopy_tuple(x, memo): - y = [] - for a in x: - y.append(deepcopy(a, memo)) - d = id(x) - try: - return memo[d] - except KeyError: - pass - for i in range(len(x)): - if x[i] is not y[i]: - y = tuple(y) - break - else: - y = x - memo[d] = y - return y + y = [] + for a in x: + y.append(deepcopy(a, memo)) + d = id(x) + try: + return memo[d] + except KeyError: + pass + for i in range(len(x)): + if x[i] is not y[i]: + y = tuple(y) + break + else: + y = x + memo[d] = y + return y d[types.TupleType] = _deepcopy_tuple def _deepcopy_dict(x, memo): - y = {} - memo[id(x)] = y - for key in x.keys(): - y[deepcopy(key, memo)] = deepcopy(x[key], memo) - return y + y = {} + memo[id(x)] = y + for key in x.keys(): + y[deepcopy(key, memo)] = deepcopy(x[key], memo) + return y d[types.DictionaryType] = _deepcopy_dict if PyStringMap is not None: d[PyStringMap] = _deepcopy_dict def _keep_alive(x, memo): - """Keeps a reference to the object x in the memo. - - Because we remember objects by their id, we have - to assure that possibly temporary objects are kept - alive by referencing them. - We store a reference at the id of the memo, which should - normally not be used unless someone tries to deepcopy - the memo itself... - """ - try: - memo[id(memo)].append(x) - except KeyError: - # aha, this is the first one :-) - memo[id(memo)]=[x] + """Keeps a reference to the object x in the memo. + + Because we remember objects by their id, we have + to assure that possibly temporary objects are kept + alive by referencing them. + We store a reference at the id of the memo, which should + normally not be used unless someone tries to deepcopy + the memo itself... + """ + try: + memo[id(memo)].append(x) + except KeyError: + # aha, this is the first one :-) + memo[id(memo)]=[x] def _deepcopy_inst(x, memo): - if hasattr(x, '__deepcopy__'): - return x.__deepcopy__(memo) - if hasattr(x, '__getinitargs__'): - args = x.__getinitargs__() - _keep_alive(args, memo) - args = deepcopy(args, memo) - y = apply(x.__class__, args) - else: - y = _EmptyClass() - y.__class__ = x.__class__ - memo[id(x)] = y - if hasattr(x, '__getstate__'): - state = x.__getstate__() - _keep_alive(state, memo) - else: - state = x.__dict__ - state = deepcopy(state, memo) - if hasattr(y, '__setstate__'): - y.__setstate__(state) - else: - y.__dict__.update(state) - return y + if hasattr(x, '__deepcopy__'): + return x.__deepcopy__(memo) + if hasattr(x, '__getinitargs__'): + args = x.__getinitargs__() + _keep_alive(args, memo) + args = deepcopy(args, memo) + y = apply(x.__class__, args) + else: + y = _EmptyClass() + y.__class__ = x.__class__ + memo[id(x)] = y + if hasattr(x, '__getstate__'): + state = x.__getstate__() + _keep_alive(state, memo) + else: + state = x.__dict__ + state = deepcopy(state, memo) + if hasattr(y, '__setstate__'): + y.__setstate__(state) + else: + y.__dict__.update(state) + return y d[types.InstanceType] = _deepcopy_inst del d @@ -260,57 +260,57 @@ class _EmptyClass: pass def _test(): - l = [None, 1, 2L, 3.14, 'xyzzy', (1, 2L), [3.14, 'abc'], - {'abc': 'ABC'}, (), [], {}] - l1 = copy(l) - print l1==l - l1 = map(copy, l) - print l1==l - l1 = deepcopy(l) - print l1==l - class C: - def __init__(self, arg=None): - self.a = 1 - self.arg = arg - if __name__ == '__main__': - import sys - file = sys.argv[0] - else: - file = __file__ - self.fp = open(file) - self.fp.close() - def __getstate__(self): - return {'a': self.a, 'arg': self.arg} - def __setstate__(self, state): - for key in state.keys(): - setattr(self, key, state[key]) - def __deepcopy__(self, memo = None): - new = self.__class__(deepcopy(self.arg, memo)) - new.a = self.a - return new - c = C('argument sketch') - l.append(c) - l2 = copy(l) - print l == l2 - print l - print l2 - l2 = deepcopy(l) - print l == l2 - print l - print l2 - l.append({l[1]: l, 'xyz': l[2]}) - l3 = copy(l) - import repr - print map(repr.repr, l) - print map(repr.repr, l1) - print map(repr.repr, l2) - print map(repr.repr, l3) - l3 = deepcopy(l) - import repr - print map(repr.repr, l) - print map(repr.repr, l1) - print map(repr.repr, l2) - print map(repr.repr, l3) + l = [None, 1, 2L, 3.14, 'xyzzy', (1, 2L), [3.14, 'abc'], + {'abc': 'ABC'}, (), [], {}] + l1 = copy(l) + print l1==l + l1 = map(copy, l) + print l1==l + l1 = deepcopy(l) + print l1==l + class C: + def __init__(self, arg=None): + self.a = 1 + self.arg = arg + if __name__ == '__main__': + import sys + file = sys.argv[0] + else: + file = __file__ + self.fp = open(file) + self.fp.close() + def __getstate__(self): + return {'a': self.a, 'arg': self.arg} + def __setstate__(self, state): + for key in state.keys(): + setattr(self, key, state[key]) + def __deepcopy__(self, memo = None): + new = self.__class__(deepcopy(self.arg, memo)) + new.a = self.a + return new + c = C('argument sketch') + l.append(c) + l2 = copy(l) + print l == l2 + print l + print l2 + l2 = deepcopy(l) + print l == l2 + print l + print l2 + l.append({l[1]: l, 'xyz': l[2]}) + l3 = copy(l) + import repr + print map(repr.repr, l) + print map(repr.repr, l1) + print map(repr.repr, l2) + print map(repr.repr, l3) + l3 = deepcopy(l) + import repr + print map(repr.repr, l) + print map(repr.repr, l1) + print map(repr.repr, l2) + print map(repr.repr, l3) if __name__ == '__main__': - _test() + _test() @@ -5,115 +5,115 @@ import string import types def dis(x=None): - """Disassemble classes, methods, functions, or code. - - With no argument, disassemble the last traceback. - - """ - if not x: - distb() - return - if type(x) is types.InstanceType: - x = x.__class__ - if hasattr(x, '__dict__'): - items = x.__dict__.items() - items.sort() - for name, x1 in items: - if type(x1) in (types.MethodType, - types.FunctionType, - types.CodeType): - print "Disassembly of %s:" % name - try: - dis(x1) - except TypeError, msg: - print "Sorry:", msg - print - else: - if hasattr(x, 'im_func'): - x = x.im_func - if hasattr(x, 'func_code'): - x = x.func_code - if hasattr(x, 'co_code'): - disassemble(x) - else: - raise TypeError, \ - "don't know how to disassemble %s objects" % \ - type(x).__name__ + """Disassemble classes, methods, functions, or code. + + With no argument, disassemble the last traceback. + + """ + if not x: + distb() + return + if type(x) is types.InstanceType: + x = x.__class__ + if hasattr(x, '__dict__'): + items = x.__dict__.items() + items.sort() + for name, x1 in items: + if type(x1) in (types.MethodType, + types.FunctionType, + types.CodeType): + print "Disassembly of %s:" % name + try: + dis(x1) + except TypeError, msg: + print "Sorry:", msg + print + else: + if hasattr(x, 'im_func'): + x = x.im_func + if hasattr(x, 'func_code'): + x = x.func_code + if hasattr(x, 'co_code'): + disassemble(x) + else: + raise TypeError, \ + "don't know how to disassemble %s objects" % \ + type(x).__name__ def distb(tb=None): - """Disassemble a traceback (default: last traceback).""" - if not tb: - try: - tb = sys.last_traceback - except AttributeError: - raise RuntimeError, "no last traceback to disassemble" - while tb.tb_next: tb = tb.tb_next - disassemble(tb.tb_frame.f_code, tb.tb_lasti) + """Disassemble a traceback (default: last traceback).""" + if not tb: + try: + tb = sys.last_traceback + except AttributeError: + raise RuntimeError, "no last traceback to disassemble" + while tb.tb_next: tb = tb.tb_next + disassemble(tb.tb_frame.f_code, tb.tb_lasti) def disassemble(co, lasti=-1): - """Disassemble a code object.""" - code = co.co_code - labels = findlabels(code) - n = len(code) - i = 0 - extended_arg = 0 - while i < n: - c = code[i] - op = ord(c) - if op == SET_LINENO and i > 0: print # Extra blank line - if i == lasti: print '-->', - else: print ' ', - if i in labels: print '>>', - else: print ' ', - print string.rjust(`i`, 4), - print string.ljust(opname[op], 20), - i = i+1 - if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg - extended_arg = 0 - i = i+2 - if op == EXTENDED_ARG: - extended_arg = oparg*65536L - print string.rjust(`oparg`, 5), - if op in hasconst: - print '(' + `co.co_consts[oparg]` + ')', - elif op in hasname: - print '(' + co.co_names[oparg] + ')', - elif op in hasjrel: - print '(to ' + `i + oparg` + ')', - elif op in haslocal: - print '(' + co.co_varnames[oparg] + ')', - elif op in hascompare: - print '(' + cmp_op[oparg] + ')', - print - -disco = disassemble # XXX For backwards compatibility + """Disassemble a code object.""" + code = co.co_code + labels = findlabels(code) + n = len(code) + i = 0 + extended_arg = 0 + while i < n: + c = code[i] + op = ord(c) + if op == SET_LINENO and i > 0: print # Extra blank line + if i == lasti: print '-->', + else: print ' ', + if i in labels: print '>>', + else: print ' ', + print string.rjust(`i`, 4), + print string.ljust(opname[op], 20), + i = i+1 + if op >= HAVE_ARGUMENT: + oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg + extended_arg = 0 + i = i+2 + if op == EXTENDED_ARG: + extended_arg = oparg*65536L + print string.rjust(`oparg`, 5), + if op in hasconst: + print '(' + `co.co_consts[oparg]` + ')', + elif op in hasname: + print '(' + co.co_names[oparg] + ')', + elif op in hasjrel: + print '(to ' + `i + oparg` + ')', + elif op in haslocal: + print '(' + co.co_varnames[oparg] + ')', + elif op in hascompare: + print '(' + cmp_op[oparg] + ')', + print + +disco = disassemble # XXX For backwards compatibility def findlabels(code): - """Detect all offsets in a byte code which are jump targets. - - Return the list of offsets. - - """ - labels = [] - n = len(code) - i = 0 - while i < n: - c = code[i] - op = ord(c) - i = i+1 - if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 - i = i+2 - label = -1 - if op in hasjrel: - label = i+oparg - elif op in hasjabs: - label = oparg - if label >= 0: - if label not in labels: - labels.append(label) - return labels + """Detect all offsets in a byte code which are jump targets. + + Return the list of offsets. + + """ + labels = [] + n = len(code) + i = 0 + while i < n: + c = code[i] + op = ord(c) + i = i+1 + if op >= HAVE_ARGUMENT: + oparg = ord(code[i]) + ord(code[i+1])*256 + i = i+2 + label = -1 + if op in hasjrel: + label = i+oparg + elif op in hasjabs: + label = oparg + if label >= 0: + if label not in labels: + labels.append(label) + return labels cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 'is not', 'exception match', 'BAD') @@ -129,19 +129,19 @@ opname = [''] * 256 for op in range(256): opname[op] = '<' + `op` + '>' def def_op(name, op): - opname[op] = name + opname[op] = name def name_op(name, op): - opname[op] = name - hasname.append(op) + opname[op] = name + hasname.append(op) def jrel_op(name, op): - opname[op] = name - hasjrel.append(op) + opname[op] = name + hasjrel.append(op) def jabs_op(name, op): - opname[op] = name - hasjabs.append(op) + opname[op] = name + hasjabs.append(op) # Instruction opcodes for compiled code @@ -219,49 +219,49 @@ def_op('POP_BLOCK', 87) def_op('END_FINALLY', 88) def_op('BUILD_CLASS', 89) -HAVE_ARGUMENT = 90 # Opcodes from here have an argument: +HAVE_ARGUMENT = 90 # Opcodes from here have an argument: -name_op('STORE_NAME', 90) # Index in name list -name_op('DELETE_NAME', 91) # "" -def_op('UNPACK_SEQUENCE', 92) # Number of tuple items +name_op('STORE_NAME', 90) # Index in name list +name_op('DELETE_NAME', 91) # "" +def_op('UNPACK_SEQUENCE', 92) # Number of tuple items -name_op('STORE_ATTR', 95) # Index in name list -name_op('DELETE_ATTR', 96) # "" -name_op('STORE_GLOBAL', 97) # "" -name_op('DELETE_GLOBAL', 98) # "" -def_op('DUP_TOPX', 99) # number of items to duplicate -def_op('LOAD_CONST', 100) # Index in const list +name_op('STORE_ATTR', 95) # Index in name list +name_op('DELETE_ATTR', 96) # "" +name_op('STORE_GLOBAL', 97) # "" +name_op('DELETE_GLOBAL', 98) # "" +def_op('DUP_TOPX', 99) # number of items to duplicate +def_op('LOAD_CONST', 100) # Index in const list hasconst.append(100) -name_op('LOAD_NAME', 101) # Index in name list -def_op('BUILD_TUPLE', 102) # Number of tuple items -def_op('BUILD_LIST', 103) # Number of list items -def_op('BUILD_MAP', 104) # Always zero for now -name_op('LOAD_ATTR', 105) # Index in name list -def_op('COMPARE_OP', 106) # Comparison operator +name_op('LOAD_NAME', 101) # Index in name list +def_op('BUILD_TUPLE', 102) # Number of tuple items +def_op('BUILD_LIST', 103) # Number of list items +def_op('BUILD_MAP', 104) # Always zero for now +name_op('LOAD_ATTR', 105) # Index in name list +def_op('COMPARE_OP', 106) # Comparison operator hascompare.append(106) -name_op('IMPORT_NAME', 107) # Index in name list -name_op('IMPORT_FROM', 108) # Index in name list +name_op('IMPORT_NAME', 107) # Index in name list +name_op('IMPORT_FROM', 108) # Index in name list -jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip -jrel_op('JUMP_IF_FALSE', 111) # "" -jrel_op('JUMP_IF_TRUE', 112) # "" -jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code -jrel_op('FOR_LOOP', 114) # Number of bytes to skip +jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip +jrel_op('JUMP_IF_FALSE', 111) # "" +jrel_op('JUMP_IF_TRUE', 112) # "" +jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code +jrel_op('FOR_LOOP', 114) # Number of bytes to skip -name_op('LOAD_GLOBAL', 116) # Index in name list +name_op('LOAD_GLOBAL', 116) # Index in name list -jrel_op('SETUP_LOOP', 120) # Distance to target address -jrel_op('SETUP_EXCEPT', 121) # "" -jrel_op('SETUP_FINALLY', 122) # "" +jrel_op('SETUP_LOOP', 120) # Distance to target address +jrel_op('SETUP_EXCEPT', 121) # "" +jrel_op('SETUP_FINALLY', 122) # "" -def_op('LOAD_FAST', 124) # Local variable number +def_op('LOAD_FAST', 124) # Local variable number haslocal.append(124) -def_op('STORE_FAST', 125) # Local variable number +def_op('STORE_FAST', 125) # Local variable number haslocal.append(125) -def_op('DELETE_FAST', 126) # Local variable number +def_op('DELETE_FAST', 126) # Local variable number haslocal.append(126) -def_op('SET_LINENO', 127) # Current line number +def_op('SET_LINENO', 127) # Current line number SET_LINENO = 127 def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) @@ -273,31 +273,31 @@ def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) -def_op('EXTENDED_ARG', 143) +def_op('EXTENDED_ARG', 143) EXTENDED_ARG = 143 def _test(): - """Simple test program to disassemble a file.""" - if sys.argv[1:]: - if sys.argv[2:]: - sys.stderr.write("usage: python dis.py [-|file]\n") - sys.exit(2) - fn = sys.argv[1] - if not fn or fn == "-": - fn = None - else: - fn = None - if not fn: - f = sys.stdin - else: - f = open(fn) - source = f.read() - if fn: - f.close() - else: - fn = "<stdin>" - code = compile(source, fn, "exec") - dis(code) + """Simple test program to disassemble a file.""" + if sys.argv[1:]: + if sys.argv[2:]: + sys.stderr.write("usage: python dis.py [-|file]\n") + sys.exit(2) + fn = sys.argv[1] + if not fn or fn == "-": + fn = None + else: + fn = None + if not fn: + f = sys.stdin + else: + f = open(fn) + source = f.read() + if fn: + f.close() + else: + fn = "<stdin>" + code = compile(source, fn, "exec") + dis(code) if __name__ == "__main__": - _test() + _test() diff --git a/Lib/dospath.py b/Lib/dospath.py index 17db624..c4846ba 100644 --- a/Lib/dospath.py +++ b/Lib/dospath.py @@ -10,8 +10,8 @@ def normcase(s): backslashes. Other normalizations (such as optimizing '../' away) are not allowed (this is done by normpath). - Previously, this version mapped invalid consecutive characters to a - single '_', but this has been removed. This functionality should + Previously, this version mapped invalid consecutive characters to a + single '_', but this has been removed. This functionality should possibly be added as a new function.""" return s.replace("/", "\\").lower() diff --git a/Lib/dumbdbm.py b/Lib/dumbdbm.py index 9ed05d6..45a2f36 100644 --- a/Lib/dumbdbm.py +++ b/Lib/dumbdbm.py @@ -28,117 +28,117 @@ _open = __builtin__.open _BLOCKSIZE = 512 -error = IOError # For anydbm +error = IOError # For anydbm class _Database: - def __init__(self, file): - self._dirfile = file + '.dir' - self._datfile = file + '.dat' - self._bakfile = file + '.bak' - # Mod by Jack: create data file if needed - try: - f = _open(self._datfile, 'r') - except IOError: - f = _open(self._datfile, 'w') - f.close() - self._update() - - def _update(self): - self._index = {} - try: - f = _open(self._dirfile) - except IOError: - pass - else: - while 1: - line = f.readline().rstrip() - if not line: break - key, (pos, siz) = eval(line) - self._index[key] = (pos, siz) - f.close() - - def _commit(self): - try: _os.unlink(self._bakfile) - except _os.error: pass - try: _os.rename(self._dirfile, self._bakfile) - except _os.error: pass - f = _open(self._dirfile, 'w') - for key, (pos, siz) in self._index.items(): - f.write("%s, (%s, %s)\n" % (`key`, `pos`, `siz`)) - f.close() - - def __getitem__(self, key): - pos, siz = self._index[key] # may raise KeyError - f = _open(self._datfile, 'rb') - f.seek(pos) - dat = f.read(siz) - f.close() - return dat - - def _addval(self, val): - f = _open(self._datfile, 'rb+') - f.seek(0, 2) - pos = int(f.tell()) + def __init__(self, file): + self._dirfile = file + '.dir' + self._datfile = file + '.dat' + self._bakfile = file + '.bak' + # Mod by Jack: create data file if needed + try: + f = _open(self._datfile, 'r') + except IOError: + f = _open(self._datfile, 'w') + f.close() + self._update() + + def _update(self): + self._index = {} + try: + f = _open(self._dirfile) + except IOError: + pass + else: + while 1: + line = f.readline().rstrip() + if not line: break + key, (pos, siz) = eval(line) + self._index[key] = (pos, siz) + f.close() + + def _commit(self): + try: _os.unlink(self._bakfile) + except _os.error: pass + try: _os.rename(self._dirfile, self._bakfile) + except _os.error: pass + f = _open(self._dirfile, 'w') + for key, (pos, siz) in self._index.items(): + f.write("%s, (%s, %s)\n" % (`key`, `pos`, `siz`)) + f.close() + + def __getitem__(self, key): + pos, siz = self._index[key] # may raise KeyError + f = _open(self._datfile, 'rb') + f.seek(pos) + dat = f.read(siz) + f.close() + return dat + + def _addval(self, val): + f = _open(self._datfile, 'rb+') + f.seek(0, 2) + pos = int(f.tell()) ## Does not work under MW compiler -## pos = ((pos + _BLOCKSIZE - 1) / _BLOCKSIZE) * _BLOCKSIZE -## f.seek(pos) - npos = ((pos + _BLOCKSIZE - 1) / _BLOCKSIZE) * _BLOCKSIZE - f.write('\0'*(npos-pos)) - pos = npos - - f.write(val) - f.close() - return (pos, len(val)) - - def _setval(self, pos, val): - f = _open(self._datfile, 'rb+') - f.seek(pos) - f.write(val) - f.close() - return (pos, len(val)) - - def _addkey(self, key, (pos, siz)): - self._index[key] = (pos, siz) - f = _open(self._dirfile, 'a') - f.write("%s, (%s, %s)\n" % (`key`, `pos`, `siz`)) - f.close() - - def __setitem__(self, key, val): - if not type(key) == type('') == type(val): - raise TypeError, "keys and values must be strings" - if not self._index.has_key(key): - (pos, siz) = self._addval(val) - self._addkey(key, (pos, siz)) - else: - pos, siz = self._index[key] - oldblocks = (siz + _BLOCKSIZE - 1) / _BLOCKSIZE - newblocks = (len(val) + _BLOCKSIZE - 1) / _BLOCKSIZE - if newblocks <= oldblocks: - pos, siz = self._setval(pos, val) - self._index[key] = pos, siz - else: - pos, siz = self._addval(val) - self._index[key] = pos, siz - - def __delitem__(self, key): - del self._index[key] - self._commit() - - def keys(self): - return self._index.keys() - - def has_key(self, key): - return self._index.has_key(key) - - def __len__(self): - return len(self._index) - - def close(self): - self._index = None - self._datfile = self._dirfile = self._bakfile = None +## pos = ((pos + _BLOCKSIZE - 1) / _BLOCKSIZE) * _BLOCKSIZE +## f.seek(pos) + npos = ((pos + _BLOCKSIZE - 1) / _BLOCKSIZE) * _BLOCKSIZE + f.write('\0'*(npos-pos)) + pos = npos + + f.write(val) + f.close() + return (pos, len(val)) + + def _setval(self, pos, val): + f = _open(self._datfile, 'rb+') + f.seek(pos) + f.write(val) + f.close() + return (pos, len(val)) + + def _addkey(self, key, (pos, siz)): + self._index[key] = (pos, siz) + f = _open(self._dirfile, 'a') + f.write("%s, (%s, %s)\n" % (`key`, `pos`, `siz`)) + f.close() + + def __setitem__(self, key, val): + if not type(key) == type('') == type(val): + raise TypeError, "keys and values must be strings" + if not self._index.has_key(key): + (pos, siz) = self._addval(val) + self._addkey(key, (pos, siz)) + else: + pos, siz = self._index[key] + oldblocks = (siz + _BLOCKSIZE - 1) / _BLOCKSIZE + newblocks = (len(val) + _BLOCKSIZE - 1) / _BLOCKSIZE + if newblocks <= oldblocks: + pos, siz = self._setval(pos, val) + self._index[key] = pos, siz + else: + pos, siz = self._addval(val) + self._index[key] = pos, siz + + def __delitem__(self, key): + del self._index[key] + self._commit() + + def keys(self): + return self._index.keys() + + def has_key(self, key): + return self._index.has_key(key) + + def __len__(self): + return len(self._index) + + def close(self): + self._index = None + self._datfile = self._dirfile = self._bakfile = None def open(file, flag = None, mode = None): - # flag, mode arguments are currently ignored - return _Database(file) + # flag, mode arguments are currently ignored + return _Database(file) diff --git a/Lib/filecmp.py b/Lib/filecmp.py index eb98a71..ec6e1ff 100644 --- a/Lib/filecmp.py +++ b/Lib/filecmp.py @@ -291,9 +291,9 @@ def cmpfiles(a, b, common, shallow=1, use_statcache=0): # Compare two files. # Return: -# 0 for equal -# 1 for different -# 2 for funny cases (can't stat, etc.) +# 0 for equal +# 1 for different +# 2 for funny cases (can't stat, etc.) # def _cmp(a, b, sh, st): try: diff --git a/Lib/fnmatch.py b/Lib/fnmatch.py index 75d2d6e..a4508bb 100644 --- a/Lib/fnmatch.py +++ b/Lib/fnmatch.py @@ -15,75 +15,75 @@ import re _cache = {} def fnmatch(name, pat): - """Test whether FILENAME matches PATTERN. - - Patterns are Unix shell style: - - * matches everything - ? matches any single character - [seq] matches any character in seq - [!seq] matches any char not in seq - - An initial period in FILENAME is not special. - Both FILENAME and PATTERN are first case-normalized - if the operating system requires it. - If you don't want this, use fnmatchcase(FILENAME, PATTERN). - """ - - import os - name = os.path.normcase(name) - pat = os.path.normcase(pat) - return fnmatchcase(name, pat) + """Test whether FILENAME matches PATTERN. + + Patterns are Unix shell style: + + * matches everything + ? matches any single character + [seq] matches any character in seq + [!seq] matches any char not in seq + + An initial period in FILENAME is not special. + Both FILENAME and PATTERN are first case-normalized + if the operating system requires it. + If you don't want this, use fnmatchcase(FILENAME, PATTERN). + """ + + import os + name = os.path.normcase(name) + pat = os.path.normcase(pat) + return fnmatchcase(name, pat) def fnmatchcase(name, pat): - """Test whether FILENAME matches PATTERN, including case. - - This is a version of fnmatch() which doesn't case-normalize - its arguments. - """ - - if not _cache.has_key(pat): - res = translate(pat) - _cache[pat] = re.compile(res) - return _cache[pat].match(name) is not None + """Test whether FILENAME matches PATTERN, including case. + + This is a version of fnmatch() which doesn't case-normalize + its arguments. + """ + + if not _cache.has_key(pat): + res = translate(pat) + _cache[pat] = re.compile(res) + return _cache[pat].match(name) is not None def translate(pat): - """Translate a shell PATTERN to a regular expression. - - There is no way to quote meta-characters. - """ - - i, n = 0, len(pat) - res = '' - while i < n: - c = pat[i] - i = i+1 - if c == '*': - res = res + '.*' - elif c == '?': - res = res + '.' - elif c == '[': - j = i - if j < n and pat[j] == '!': - j = j+1 - if j < n and pat[j] == ']': - j = j+1 - while j < n and pat[j] != ']': - j = j+1 - if j >= n: - res = res + '\\[' - else: - stuff = pat[i:j] - i = j+1 - if stuff[0] == '!': - stuff = '[^' + stuff[1:] + ']' - elif stuff == '^'*len(stuff): - stuff = '\\^' - else: - while stuff[0] == '^': - stuff = stuff[1:] + stuff[0] - stuff = '[' + stuff + ']' - res = res + stuff - else: - res = res + re.escape(c) - return res + "$" + """Translate a shell PATTERN to a regular expression. + + There is no way to quote meta-characters. + """ + + i, n = 0, len(pat) + res = '' + while i < n: + c = pat[i] + i = i+1 + if c == '*': + res = res + '.*' + elif c == '?': + res = res + '.' + elif c == '[': + j = i + if j < n and pat[j] == '!': + j = j+1 + if j < n and pat[j] == ']': + j = j+1 + while j < n and pat[j] != ']': + j = j+1 + if j >= n: + res = res + '\\[' + else: + stuff = pat[i:j] + i = j+1 + if stuff[0] == '!': + stuff = '[^' + stuff[1:] + ']' + elif stuff == '^'*len(stuff): + stuff = '\\^' + else: + while stuff[0] == '^': + stuff = stuff[1:] + stuff[0] + stuff = '[' + stuff + ']' + res = res + stuff + else: + res = res + re.escape(c) + return res + "$" diff --git a/Lib/formatter.py b/Lib/formatter.py index 4d6a129..359975b 100644 --- a/Lib/formatter.py +++ b/Lib/formatter.py @@ -9,13 +9,13 @@ controlled via formatter objects are horizontal alignment, font, and left margin indentations. A mechanism is provided which supports providing arbitrary, non-exclusive style settings to a writer as well. Additional interfaces facilitate formatting events which are not reversible, such as -paragraph separation. +paragraph separation. Writer objects encapsulate device interfaces. Abstract devices, such as file formats, are supported as well as physical devices. The provided implementations all work with abstract devices. The interface makes available mechanisms for setting the properties which formatter objects -manage and inserting data into the output. +manage and inserting data into the output. """ import string diff --git a/Lib/fpformat.py b/Lib/fpformat.py index 31debba..df2092f 100644 --- a/Lib/fpformat.py +++ b/Lib/fpformat.py @@ -138,4 +138,3 @@ def test(): print x, fix(x, digs), sci(x, digs) except (EOFError, KeyboardInterrupt): pass - diff --git a/Lib/ftplib.py b/Lib/ftplib.py index df28200..5d0e260 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -22,13 +22,13 @@ drwxr-xr-x 3 root wheel 1024 Jan 3 1994 usr '226 Transfer complete.' >>> ftp.quit() '221 Goodbye.' ->>> +>>> A nice test that reveals some of the network dialogue would be: python ftplib.py -d localhost -l -p -l """ -# +# # Changes and improvements suggested by Steve Majewski. # Modified by Jack to work on the mac. # Modified by Siebren to support docstrings and PASV. @@ -40,14 +40,14 @@ import string # Import SOCKS module if it exists, else standard socket module socket try: - import SOCKS; socket = SOCKS; del SOCKS # import SOCKS as socket - from socket import getfqdn; socket.getfqdn = getfqdn; del getfqdn + import SOCKS; socket = SOCKS; del SOCKS # import SOCKS as socket + from socket import getfqdn; socket.getfqdn = getfqdn; del getfqdn except ImportError: - import socket + import socket # Magic number from <socket.h> -MSG_OOB = 0x1 # Process data out of band +MSG_OOB = 0x1 # Process data out of band # The standard FTP server control port @@ -56,10 +56,10 @@ FTP_PORT = 21 # Exception raised when an error or invalid response is received class Error(Exception): pass -class error_reply(Error): pass # unexpected [123]xx reply -class error_temp(Error): pass # 4xx errors -class error_perm(Error): pass # 5xx errors -class error_proto(Error): pass # response does not begin with [1-5] +class error_reply(Error): pass # unexpected [123]xx reply +class error_temp(Error): pass # 4xx errors +class error_perm(Error): pass # 5xx errors +class error_proto(Error): pass # response does not begin with [1-5] # All exceptions (hopefully) that may be raised here and that aren't @@ -74,654 +74,654 @@ CRLF = '\r\n' # The class itself class FTP: - '''An FTP client class. + '''An FTP client class. - To create a connection, call the class using these argument: - host, user, passwd, acct - These are all strings, and have default value ''. - Then use self.connect() with optional host and port argument. + To create a connection, call the class using these argument: + host, user, passwd, acct + These are all strings, and have default value ''. + Then use self.connect() with optional host and port argument. - To download a file, use ftp.retrlines('RETR ' + filename), - or ftp.retrbinary() with slightly different arguments. - To upload a file, use ftp.storlines() or ftp.storbinary(), - which have an open file as argument (see their definitions - below for details). - The download/upload functions first issue appropriate TYPE - and PORT or PASV commands. + To download a file, use ftp.retrlines('RETR ' + filename), + or ftp.retrbinary() with slightly different arguments. + To upload a file, use ftp.storlines() or ftp.storbinary(), + which have an open file as argument (see their definitions + below for details). + The download/upload functions first issue appropriate TYPE + and PORT or PASV commands. ''' - # 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 = ''): - # Initialize the instance to something mostly harmless - self.debugging = 0 - self.host = '' - self.port = FTP_PORT - self.sock = None - self.file = None - self.welcome = None - resp = None - if host: - resp = self.connect(host) - if user: resp = self.login(user, passwd, acct) - - def connect(self, host = '', port = 0): - '''Connect to host. Arguments are: - - host: hostname to connect to (string, default previous host) - - port: port to connect to (integer, default previous port)''' - if host: self.host = host - if port: self.port = port - self.passiveserver = 0 - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((self.host, self.port)) - self.file = self.sock.makefile('rb') - self.welcome = self.getresp() - return self.welcome - - def getwelcome(self): - '''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. - 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''' - self.debugging = level - debug = set_debuglevel - - def set_pasv(self, val): - '''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.''' - self.passiveserver = val - - # Internal: "sanitize" a string for printing - def sanitize(self, s): - if s[:5] == 'pass ' or s[:5] == 'PASS ': - i = len(s) - while i > 5 and s[i-1] in '\r\n': - i = i-1 - s = s[:5] + '*'*(i-5) + s[i:] - return `s` - - # Internal: send one line to the server, appending CRLF - def putline(self, line): - line = line + CRLF - if self.debugging > 1: print '*put*', self.sanitize(line) - self.sock.send(line) - - # Internal: send one command to the server (through putline()) - def putcmd(self, line): - 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): - line = self.file.readline() - if self.debugging > 1: - print '*get*', self.sanitize(line) - if not line: raise EOFError - if line[-2:] == CRLF: line = line[:-2] - elif line[-1:] in CRLF: 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): - line = self.getline() - if line[3:4] == '-': - code = line[:3] - while 1: - nextline = self.getline() - line = line + ('\n' + nextline) - if nextline[:3] == code and \ - nextline[3:4] != '-': - break - return line - - # Internal: get a response from the server. - # Raise various errors if the response indicates an error - def getresp(self): - resp = self.getmultiline() - if self.debugging: print '*resp*', self.sanitize(resp) - self.lastresp = resp[:3] - c = resp[:1] - if c == '4': - raise error_temp, resp - if c == '5': - raise error_perm, resp - if c not in '123': - raise error_proto, resp - return resp - - def voidresp(self): - """Expect a response beginning with '2'.""" - resp = self.getresp() - if resp[0] != '2': - raise error_reply, resp - return resp - - def abort(self): - '''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.''' - line = 'ABOR' + CRLF - if self.debugging > 1: print '*put urgent*', self.sanitize(line) - self.sock.send(line, MSG_OOB) - resp = self.getmultiline() - if resp[:3] not in ('426', '226'): - raise error_proto, resp - - def sendcmd(self, cmd): - '''Send a command and return the response.''' - self.putcmd(cmd) - return self.getresp() - - def voidcmd(self, cmd): - """Send a command and expect a response beginning with '2'.""" - self.putcmd(cmd) - return self.voidresp() - - def sendport(self, host, port): - '''Send a PORT command with the current host and the given - port number. - ''' - hbytes = string.splitfields(host, '.') - pbytes = [`port/256`, `port%256`] - bytes = hbytes + pbytes - cmd = 'PORT ' + string.joinfields(bytes, ',') - return self.voidcmd(cmd) - - def makeport(self): - '''Create a new socket and send a PORT command for it.''' - global nextport - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.bind(('', 0)) - sock.listen(1) - dummyhost, port = sock.getsockname() # Get proper port - host, dummyport = self.sock.getsockname() # Get proper host - resp = self.sendport(host, port) - return sock - - def ntransfercmd(self, cmd, rest=None): - """Initiate a transfer over the data connection. - - If the transfer is active, send a port command and the - transfer command, and accept the connection. If the server is - passive, send a pasv command, connect to it, and start the - transfer command. Either way, return the socket for the - connection and the expected size of the transfer. The - expected size may be None if it could not be determined. - - Optional `rest' argument can be a string that is sent as the - argument to a RESTART command. This is essentially a server - marker used to tell the server to skip over any data up to the - given marker. - """ - size = None - if self.passiveserver: - host, port = parse227(self.sendcmd('PASV')) - conn=socket.socket(socket.AF_INET, socket.SOCK_STREAM) - conn.connect((host, port)) - if rest is not None: - self.sendcmd("REST %s" % rest) - resp = self.sendcmd(cmd) - if resp[0] != '1': - raise error_reply, resp - else: - sock = self.makeport() - if rest is not None: - self.sendcmd("REST %s" % rest) - resp = self.sendcmd(cmd) - if resp[0] != '1': - raise error_reply, resp - conn, sockaddr = sock.accept() - if resp[:3] == '150': - # this is conditional in case we received a 125 - size = parse150(resp) - return conn, size - - def transfercmd(self, cmd, rest=None): - """Like nstransfercmd() but returns only the socket.""" - return self.ntransfercmd(cmd, rest)[0] - - def login(self, user = '', passwd = '', acct = ''): - '''Login, default anonymous.''' - if not user: user = 'anonymous' - if not passwd: passwd = '' - if not acct: acct = '' - if user == 'anonymous' and passwd in ('', '-'): - # get fully qualified domain name of local host - thishost = socket.getfqdn() - try: - if os.environ.has_key('LOGNAME'): - realuser = os.environ['LOGNAME'] - elif os.environ.has_key('USER'): - realuser = os.environ['USER'] - else: - realuser = 'anonymous' - except AttributeError: - # Not all systems have os.environ.... - realuser = 'anonymous' - passwd = passwd + realuser + '@' + thishost - resp = self.sendcmd('USER ' + user) - if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd) - if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct) - if resp[0] != '2': - raise error_reply, resp - return resp - - def retrbinary(self, cmd, callback, blocksize=8192, rest=None): - """Retrieve data in binary mode. - - `cmd' is a RETR command. `callback' is a callback function is - called for each block. No more than `blocksize' number of - bytes will be read from the socket. Optional `rest' is passed - to transfercmd(). - - A new port is created for you. Return the response code. - """ - self.voidcmd('TYPE I') - conn = self.transfercmd(cmd, rest) - while 1: - data = conn.recv(blocksize) - if not data: - break - callback(data) - conn.close() - return self.voidresp() - - def retrlines(self, cmd, callback = None): - '''Retrieve data in line mode. - The argument is a RETR or LIST command. - The callback function (2nd argument) is called for each line, - with trailing CRLF stripped. This creates a new port for you. - print_line() is the default callback.''' - if not callback: callback = print_line - resp = self.sendcmd('TYPE A') - conn = self.transfercmd(cmd) - fp = conn.makefile('rb') - while 1: - line = fp.readline() - if self.debugging > 2: print '*retr*', `line` - if not line: - break - if line[-2:] == CRLF: - line = line[:-2] - elif line[-1:] == '\n': - line = line[:-1] - callback(line) - fp.close() - conn.close() - return self.voidresp() - - def storbinary(self, cmd, fp, blocksize): - '''Store a file in binary mode.''' - self.voidcmd('TYPE I') - conn = self.transfercmd(cmd) - while 1: - buf = fp.read(blocksize) - if not buf: break - conn.send(buf) - conn.close() - return self.voidresp() - - def storlines(self, cmd, fp): - '''Store a file in line mode.''' - self.voidcmd('TYPE A') - conn = self.transfercmd(cmd) - while 1: - buf = fp.readline() - if not buf: break - if buf[-2:] != CRLF: - if buf[-1] in CRLF: buf = buf[:-1] - buf = buf + CRLF - conn.send(buf) - conn.close() - return self.voidresp() - - def acct(self, password): - '''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).''' - cmd = 'NLST' - for arg in args: - cmd = cmd + (' ' + arg) - files = [] - self.retrlines(cmd, files.append) - return files - - def dir(self, *args): - '''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.)''' - cmd = 'LIST' - func = None - if args[-1:] and type(args[-1]) != type(''): - args, func = args[:-1], args[-1] - for arg in args: - if arg: - cmd = cmd + (' ' + arg) - self.retrlines(cmd, func) - - def rename(self, fromname, toname): - '''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.''' - resp = self.sendcmd('DELE ' + filename) - if resp[:3] in ('250', '200'): - return resp - elif resp[:1] == '5': - raise error_perm, resp - else: - raise error_reply, resp - - def cwd(self, dirname): - '''Change to a directory.''' - if dirname == '..': - try: - return self.voidcmd('CDUP') - except error_perm, msg: - if msg[:3] != '500': - raise error_perm, msg - elif dirname == '': - dirname = '.' # does nothing, but could return error - cmd = 'CWD ' + dirname - return self.voidcmd(cmd) - - def size(self, filename): - '''Retrieve the size of a file.''' - # Note that the RFC doesn't say anything about 'SIZE' - resp = self.sendcmd('SIZE ' + filename) - if resp[:3] == '213': - return string.atoi(string.strip(resp[3:])) - - def mkd(self, dirname): - '''Make a directory, return its full pathname.''' - resp = self.sendcmd('MKD ' + dirname) - return parse257(resp) - - def rmd(self, dirname): - '''Remove a directory.''' - return self.voidcmd('RMD ' + dirname) - - def pwd(self): - '''Return current working directory.''' - resp = self.sendcmd('PWD') - return parse257(resp) - - def quit(self): - '''Quit, and close the connection.''' - resp = self.voidcmd('QUIT') - self.close() - return resp - - def close(self): - '''Close the connection without assuming anything about it.''' - self.file.close() - self.sock.close() - del self.file, self.sock + # 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 = ''): + # Initialize the instance to something mostly harmless + self.debugging = 0 + self.host = '' + self.port = FTP_PORT + self.sock = None + self.file = None + self.welcome = None + resp = None + if host: + resp = self.connect(host) + if user: resp = self.login(user, passwd, acct) + + def connect(self, host = '', port = 0): + '''Connect to host. Arguments are: + - host: hostname to connect to (string, default previous host) + - port: port to connect to (integer, default previous port)''' + if host: self.host = host + if port: self.port = port + self.passiveserver = 0 + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((self.host, self.port)) + self.file = self.sock.makefile('rb') + self.welcome = self.getresp() + return self.welcome + + def getwelcome(self): + '''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. + 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''' + self.debugging = level + debug = set_debuglevel + + def set_pasv(self, val): + '''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.''' + self.passiveserver = val + + # Internal: "sanitize" a string for printing + def sanitize(self, s): + if s[:5] == 'pass ' or s[:5] == 'PASS ': + i = len(s) + while i > 5 and s[i-1] in '\r\n': + i = i-1 + s = s[:5] + '*'*(i-5) + s[i:] + return `s` + + # Internal: send one line to the server, appending CRLF + def putline(self, line): + line = line + CRLF + if self.debugging > 1: print '*put*', self.sanitize(line) + self.sock.send(line) + + # Internal: send one command to the server (through putline()) + def putcmd(self, line): + 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): + line = self.file.readline() + if self.debugging > 1: + print '*get*', self.sanitize(line) + if not line: raise EOFError + if line[-2:] == CRLF: line = line[:-2] + elif line[-1:] in CRLF: 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): + line = self.getline() + if line[3:4] == '-': + code = line[:3] + while 1: + nextline = self.getline() + line = line + ('\n' + nextline) + if nextline[:3] == code and \ + nextline[3:4] != '-': + break + return line + + # Internal: get a response from the server. + # Raise various errors if the response indicates an error + def getresp(self): + resp = self.getmultiline() + if self.debugging: print '*resp*', self.sanitize(resp) + self.lastresp = resp[:3] + c = resp[:1] + if c == '4': + raise error_temp, resp + if c == '5': + raise error_perm, resp + if c not in '123': + raise error_proto, resp + return resp + + def voidresp(self): + """Expect a response beginning with '2'.""" + resp = self.getresp() + if resp[0] != '2': + raise error_reply, resp + return resp + + def abort(self): + '''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.''' + line = 'ABOR' + CRLF + if self.debugging > 1: print '*put urgent*', self.sanitize(line) + self.sock.send(line, MSG_OOB) + resp = self.getmultiline() + if resp[:3] not in ('426', '226'): + raise error_proto, resp + + def sendcmd(self, cmd): + '''Send a command and return the response.''' + self.putcmd(cmd) + return self.getresp() + + def voidcmd(self, cmd): + """Send a command and expect a response beginning with '2'.""" + self.putcmd(cmd) + return self.voidresp() + + def sendport(self, host, port): + '''Send a PORT command with the current host and the given + port number. + ''' + hbytes = string.splitfields(host, '.') + pbytes = [`port/256`, `port%256`] + bytes = hbytes + pbytes + cmd = 'PORT ' + string.joinfields(bytes, ',') + return self.voidcmd(cmd) + + def makeport(self): + '''Create a new socket and send a PORT command for it.''' + global nextport + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('', 0)) + sock.listen(1) + dummyhost, port = sock.getsockname() # Get proper port + host, dummyport = self.sock.getsockname() # Get proper host + resp = self.sendport(host, port) + return sock + + def ntransfercmd(self, cmd, rest=None): + """Initiate a transfer over the data connection. + + If the transfer is active, send a port command and the + transfer command, and accept the connection. If the server is + passive, send a pasv command, connect to it, and start the + transfer command. Either way, return the socket for the + connection and the expected size of the transfer. The + expected size may be None if it could not be determined. + + Optional `rest' argument can be a string that is sent as the + argument to a RESTART command. This is essentially a server + marker used to tell the server to skip over any data up to the + given marker. + """ + size = None + if self.passiveserver: + host, port = parse227(self.sendcmd('PASV')) + conn=socket.socket(socket.AF_INET, socket.SOCK_STREAM) + conn.connect((host, port)) + if rest is not None: + self.sendcmd("REST %s" % rest) + resp = self.sendcmd(cmd) + if resp[0] != '1': + raise error_reply, resp + else: + sock = self.makeport() + if rest is not None: + self.sendcmd("REST %s" % rest) + resp = self.sendcmd(cmd) + if resp[0] != '1': + raise error_reply, resp + conn, sockaddr = sock.accept() + if resp[:3] == '150': + # this is conditional in case we received a 125 + size = parse150(resp) + return conn, size + + def transfercmd(self, cmd, rest=None): + """Like nstransfercmd() but returns only the socket.""" + return self.ntransfercmd(cmd, rest)[0] + + def login(self, user = '', passwd = '', acct = ''): + '''Login, default anonymous.''' + if not user: user = 'anonymous' + if not passwd: passwd = '' + if not acct: acct = '' + if user == 'anonymous' and passwd in ('', '-'): + # get fully qualified domain name of local host + thishost = socket.getfqdn() + try: + if os.environ.has_key('LOGNAME'): + realuser = os.environ['LOGNAME'] + elif os.environ.has_key('USER'): + realuser = os.environ['USER'] + else: + realuser = 'anonymous' + except AttributeError: + # Not all systems have os.environ.... + realuser = 'anonymous' + passwd = passwd + realuser + '@' + thishost + resp = self.sendcmd('USER ' + user) + if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd) + if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct) + if resp[0] != '2': + raise error_reply, resp + return resp + + def retrbinary(self, cmd, callback, blocksize=8192, rest=None): + """Retrieve data in binary mode. + + `cmd' is a RETR command. `callback' is a callback function is + called for each block. No more than `blocksize' number of + bytes will be read from the socket. Optional `rest' is passed + to transfercmd(). + + A new port is created for you. Return the response code. + """ + self.voidcmd('TYPE I') + conn = self.transfercmd(cmd, rest) + while 1: + data = conn.recv(blocksize) + if not data: + break + callback(data) + conn.close() + return self.voidresp() + + def retrlines(self, cmd, callback = None): + '''Retrieve data in line mode. + The argument is a RETR or LIST command. + The callback function (2nd argument) is called for each line, + with trailing CRLF stripped. This creates a new port for you. + print_line() is the default callback.''' + if not callback: callback = print_line + resp = self.sendcmd('TYPE A') + conn = self.transfercmd(cmd) + fp = conn.makefile('rb') + while 1: + line = fp.readline() + if self.debugging > 2: print '*retr*', `line` + if not line: + break + if line[-2:] == CRLF: + line = line[:-2] + elif line[-1:] == '\n': + line = line[:-1] + callback(line) + fp.close() + conn.close() + return self.voidresp() + + def storbinary(self, cmd, fp, blocksize): + '''Store a file in binary mode.''' + self.voidcmd('TYPE I') + conn = self.transfercmd(cmd) + while 1: + buf = fp.read(blocksize) + if not buf: break + conn.send(buf) + conn.close() + return self.voidresp() + + def storlines(self, cmd, fp): + '''Store a file in line mode.''' + self.voidcmd('TYPE A') + conn = self.transfercmd(cmd) + while 1: + buf = fp.readline() + if not buf: break + if buf[-2:] != CRLF: + if buf[-1] in CRLF: buf = buf[:-1] + buf = buf + CRLF + conn.send(buf) + conn.close() + return self.voidresp() + + def acct(self, password): + '''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).''' + cmd = 'NLST' + for arg in args: + cmd = cmd + (' ' + arg) + files = [] + self.retrlines(cmd, files.append) + return files + + def dir(self, *args): + '''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.)''' + cmd = 'LIST' + func = None + if args[-1:] and type(args[-1]) != type(''): + args, func = args[:-1], args[-1] + for arg in args: + if arg: + cmd = cmd + (' ' + arg) + self.retrlines(cmd, func) + + def rename(self, fromname, toname): + '''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.''' + resp = self.sendcmd('DELE ' + filename) + if resp[:3] in ('250', '200'): + return resp + elif resp[:1] == '5': + raise error_perm, resp + else: + raise error_reply, resp + + def cwd(self, dirname): + '''Change to a directory.''' + if dirname == '..': + try: + return self.voidcmd('CDUP') + except error_perm, msg: + if msg[:3] != '500': + raise error_perm, msg + elif dirname == '': + dirname = '.' # does nothing, but could return error + cmd = 'CWD ' + dirname + return self.voidcmd(cmd) + + def size(self, filename): + '''Retrieve the size of a file.''' + # Note that the RFC doesn't say anything about 'SIZE' + resp = self.sendcmd('SIZE ' + filename) + if resp[:3] == '213': + return string.atoi(string.strip(resp[3:])) + + def mkd(self, dirname): + '''Make a directory, return its full pathname.''' + resp = self.sendcmd('MKD ' + dirname) + return parse257(resp) + + def rmd(self, dirname): + '''Remove a directory.''' + return self.voidcmd('RMD ' + dirname) + + def pwd(self): + '''Return current working directory.''' + resp = self.sendcmd('PWD') + return parse257(resp) + + def quit(self): + '''Quit, and close the connection.''' + resp = self.voidcmd('QUIT') + self.close() + return resp + + def close(self): + '''Close the connection without assuming anything about it.''' + self.file.close() + self.sock.close() + del self.file, self.sock _150_re = None def parse150(resp): - '''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 - if _150_re is None: - import re - _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE) - m = _150_re.match(resp) - if m: - return string.atoi(m.group(1)) - return None + '''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 + if _150_re is None: + import re + _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE) + m = _150_re.match(resp) + if m: + return string.atoi(m.group(1)) + return None def parse227(resp): - '''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.''' - - if resp[:3] != '227': - raise error_reply, resp - left = string.find(resp, '(') - if left < 0: raise error_proto, resp - right = string.find(resp, ')', left + 1) - if right < 0: - raise error_proto, resp # should contain '(h1,h2,h3,h4,p1,p2)' - numbers = string.split(resp[left+1:right], ',') - if len(numbers) != 6: - raise error_proto, resp - host = string.join(numbers[:4], '.') - port = (string.atoi(numbers[4]) << 8) + string.atoi(numbers[5]) - return host, port + '''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.''' + + if resp[:3] != '227': + raise error_reply, resp + left = string.find(resp, '(') + if left < 0: raise error_proto, resp + right = string.find(resp, ')', left + 1) + if right < 0: + raise error_proto, resp # should contain '(h1,h2,h3,h4,p1,p2)' + numbers = string.split(resp[left+1:right], ',') + if len(numbers) != 6: + raise error_proto, resp + host = string.join(numbers[:4], '.') + port = (string.atoi(numbers[4]) << 8) + string.atoi(numbers[5]) + return host, port def parse257(resp): - '''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.''' - - if resp[:3] != '257': - raise error_reply, resp - if resp[3:5] != ' "': - return '' # Not compliant to RFC 959, but UNIX ftpd does this - dirname = '' - i = 5 - n = len(resp) - while i < n: - c = resp[i] - i = i+1 - if c == '"': - if i >= n or resp[i] != '"': - break - i = i+1 - dirname = dirname + c - return dirname + '''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.''' + + if resp[:3] != '257': + raise error_reply, resp + if resp[3:5] != ' "': + return '' # Not compliant to RFC 959, but UNIX ftpd does this + dirname = '' + i = 5 + n = len(resp) + while i < n: + c = resp[i] + i = i+1 + if c == '"': + if i >= n or resp[i] != '"': + break + i = i+1 + dirname = dirname + c + return dirname def print_line(line): - '''Default retrlines callback to print a line.''' - print 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.''' - if not targetname: targetname = sourcename - type = 'TYPE ' + type - source.voidcmd(type) - target.voidcmd(type) - sourcehost, sourceport = parse227(source.sendcmd('PASV')) - target.sendport(sourcehost, sourceport) - # RFC 959: the user must "listen" [...] BEFORE sending the - # transfer request. - # So: STOR before RETR, because here the target is a "user". - treply = target.sendcmd('STOR ' + targetname) - if treply[:3] not in ('125', '150'): raise error_proto # RFC 959 - sreply = source.sendcmd('RETR ' + sourcename) - if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959 - source.voidresp() - target.voidresp() - - + '''Copy file from one FTP-instance to another.''' + if not targetname: targetname = sourcename + type = 'TYPE ' + type + source.voidcmd(type) + target.voidcmd(type) + sourcehost, sourceport = parse227(source.sendcmd('PASV')) + target.sendport(sourcehost, sourceport) + # RFC 959: the user must "listen" [...] BEFORE sending the + # transfer request. + # So: STOR before RETR, because here the target is a "user". + treply = target.sendcmd('STOR ' + targetname) + if treply[:3] not in ('125', '150'): raise error_proto # RFC 959 + sreply = source.sendcmd('RETR ' + sourcename) + if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959 + source.voidresp() + target.voidresp() + + class Netrc: - """Class to parse & provide access to 'netrc' format files. - - See the netrc(4) man page for information on the file format. - - WARNING: This class is obsolete -- use module netrc instead. - - """ - __defuser = None - __defpasswd = None - __defacct = None - - def __init__(self, filename=None): - if not filename: - if os.environ.has_key("HOME"): - filename = os.path.join(os.environ["HOME"], - ".netrc") - else: - raise IOError, \ - "specify file to load or set $HOME" - self.__hosts = {} - self.__macros = {} - fp = open(filename, "r") - in_macro = 0 - while 1: - line = fp.readline() - if not line: break - if in_macro and string.strip(line): - macro_lines.append(line) - continue - elif in_macro: - self.__macros[macro_name] = tuple(macro_lines) - in_macro = 0 - words = string.split(line) - host = user = passwd = acct = None - default = 0 - i = 0 - while i < len(words): - w1 = words[i] - if i+1 < len(words): - w2 = words[i + 1] - else: - w2 = None - if w1 == 'default': - default = 1 - elif w1 == 'machine' and w2: - host = string.lower(w2) - i = i + 1 - elif w1 == 'login' and w2: - user = w2 - i = i + 1 - elif w1 == 'password' and w2: - passwd = w2 - i = i + 1 - elif w1 == 'account' and w2: - acct = w2 - i = i + 1 - elif w1 == 'macdef' and w2: - macro_name = w2 - macro_lines = [] - in_macro = 1 - break - i = i + 1 - if default: - self.__defuser = user or self.__defuser - self.__defpasswd = passwd or self.__defpasswd - self.__defacct = acct or self.__defacct - if host: - if self.__hosts.has_key(host): - ouser, opasswd, oacct = \ - self.__hosts[host] - user = user or ouser - passwd = passwd or opasswd - acct = acct or oacct - self.__hosts[host] = user, passwd, acct - fp.close() - - def get_hosts(self): - """Return a list of hosts mentioned in the .netrc file.""" - return self.__hosts.keys() - - def get_account(self, host): - """Returns login information for the named host. - - The return value is a triple containing userid, - password, and the accounting field. - - """ - host = string.lower(host) - user = passwd = acct = None - if self.__hosts.has_key(host): - user, passwd, acct = self.__hosts[host] - user = user or self.__defuser - passwd = passwd or self.__defpasswd - acct = acct or self.__defacct - return user, passwd, acct - - def get_macros(self): - """Return a list of all defined macro names.""" - return self.__macros.keys() - - def get_macro(self, macro): - """Return a sequence of lines which define a named macro.""" - return self.__macros[macro] - - - + """Class to parse & provide access to 'netrc' format files. + + See the netrc(4) man page for information on the file format. + + WARNING: This class is obsolete -- use module netrc instead. + + """ + __defuser = None + __defpasswd = None + __defacct = None + + def __init__(self, filename=None): + if not filename: + if os.environ.has_key("HOME"): + filename = os.path.join(os.environ["HOME"], + ".netrc") + else: + raise IOError, \ + "specify file to load or set $HOME" + self.__hosts = {} + self.__macros = {} + fp = open(filename, "r") + in_macro = 0 + while 1: + line = fp.readline() + if not line: break + if in_macro and string.strip(line): + macro_lines.append(line) + continue + elif in_macro: + self.__macros[macro_name] = tuple(macro_lines) + in_macro = 0 + words = string.split(line) + host = user = passwd = acct = None + default = 0 + i = 0 + while i < len(words): + w1 = words[i] + if i+1 < len(words): + w2 = words[i + 1] + else: + w2 = None + if w1 == 'default': + default = 1 + elif w1 == 'machine' and w2: + host = string.lower(w2) + i = i + 1 + elif w1 == 'login' and w2: + user = w2 + i = i + 1 + elif w1 == 'password' and w2: + passwd = w2 + i = i + 1 + elif w1 == 'account' and w2: + acct = w2 + i = i + 1 + elif w1 == 'macdef' and w2: + macro_name = w2 + macro_lines = [] + in_macro = 1 + break + i = i + 1 + if default: + self.__defuser = user or self.__defuser + self.__defpasswd = passwd or self.__defpasswd + self.__defacct = acct or self.__defacct + if host: + if self.__hosts.has_key(host): + ouser, opasswd, oacct = \ + self.__hosts[host] + user = user or ouser + passwd = passwd or opasswd + acct = acct or oacct + self.__hosts[host] = user, passwd, acct + fp.close() + + def get_hosts(self): + """Return a list of hosts mentioned in the .netrc file.""" + return self.__hosts.keys() + + def get_account(self, host): + """Returns login information for the named host. + + The return value is a triple containing userid, + password, and the accounting field. + + """ + host = string.lower(host) + user = passwd = acct = None + if self.__hosts.has_key(host): + user, passwd, acct = self.__hosts[host] + user = user or self.__defuser + passwd = passwd or self.__defpasswd + acct = acct or self.__defacct + return user, passwd, acct + + def get_macros(self): + """Return a list of all defined macro names.""" + return self.__macros.keys() + + def get_macro(self, macro): + """Return a sequence of lines which define a named macro.""" + return self.__macros[macro] + + + def test(): - '''Test program. - Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...''' - - debugging = 0 - rcfile = None - while sys.argv[1] == '-d': - debugging = debugging+1 - del sys.argv[1] - if sys.argv[1][:2] == '-r': - # get name of alternate ~/.netrc file: - rcfile = sys.argv[1][2:] - del sys.argv[1] - host = sys.argv[1] - ftp = FTP(host) - ftp.set_debuglevel(debugging) - userid = passwd = acct = '' - try: - netrc = Netrc(rcfile) - except IOError: - if rcfile is not None: - sys.stderr.write("Could not open account file" - " -- using anonymous login.") - else: - try: - userid, passwd, acct = netrc.get_account(host) - except KeyError: - # no account for host - sys.stderr.write( - "No account -- using anonymous login.") - ftp.login(userid, passwd, acct) - for file in sys.argv[2:]: - if file[:2] == '-l': - ftp.dir(file[2:]) - elif file[:2] == '-d': - cmd = 'CWD' - if file[2:]: cmd = cmd + ' ' + file[2:] - resp = ftp.sendcmd(cmd) - elif file == '-p': - ftp.set_pasv(not ftp.passiveserver) - else: - ftp.retrbinary('RETR ' + file, \ - sys.stdout.write, 1024) - ftp.quit() + '''Test program. + Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...''' + + debugging = 0 + rcfile = None + while sys.argv[1] == '-d': + debugging = debugging+1 + del sys.argv[1] + if sys.argv[1][:2] == '-r': + # get name of alternate ~/.netrc file: + rcfile = sys.argv[1][2:] + del sys.argv[1] + host = sys.argv[1] + ftp = FTP(host) + ftp.set_debuglevel(debugging) + userid = passwd = acct = '' + try: + netrc = Netrc(rcfile) + except IOError: + if rcfile is not None: + sys.stderr.write("Could not open account file" + " -- using anonymous login.") + else: + try: + userid, passwd, acct = netrc.get_account(host) + except KeyError: + # no account for host + sys.stderr.write( + "No account -- using anonymous login.") + ftp.login(userid, passwd, acct) + for file in sys.argv[2:]: + if file[:2] == '-l': + ftp.dir(file[2:]) + elif file[:2] == '-d': + cmd = 'CWD' + if file[2:]: cmd = cmd + ' ' + file[2:] + resp = ftp.sendcmd(cmd) + elif file == '-p': + ftp.set_pasv(not ftp.passiveserver) + else: + ftp.retrbinary('RETR ' + file, \ + sys.stdout.write, 1024) + ftp.quit() if __name__ == '__main__': - test() + test() |