From 07e99cb77406e1bc84606f49b743e41b0de8a6d5 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 14 Jan 2001 23:47:14 +0000 Subject: Whitespace normalization. --- Lib/MimeWriter.py | 2 +- Lib/getpass.py | 151 +++-- Lib/gettext.py | 14 +- Lib/glob.py | 82 +-- Lib/gzip.py | 50 +- Lib/htmlentitydefs.py | 504 +++++++-------- Lib/htmllib.py | 2 +- Lib/httplib.py | 34 +- Lib/ihooks.py | 4 +- Lib/imaplib.py | 1637 ++++++++++++++++++++++++------------------------- Lib/imghdr.py | 4 +- Lib/imputil.py | 12 +- Lib/macurl2path.py | 10 +- Lib/mailcap.py | 6 +- Lib/mimetools.py | 352 +++++------ Lib/mimify.py | 797 ++++++++++++------------ Lib/multifile.py | 260 ++++---- Lib/mutex.py | 64 +- 18 files changed, 1991 insertions(+), 1994 deletions(-) diff --git a/Lib/MimeWriter.py b/Lib/MimeWriter.py index 754576b..9f04656 100644 --- a/Lib/MimeWriter.py +++ b/Lib/MimeWriter.py @@ -30,7 +30,7 @@ class MimeWriter: amounts of buffer space, so you have to write the parts in the order they should occur on the output file. It does buffer the headers you add, allowing you to rearrange their order. - + General usage is: f = diff --git a/Lib/getpass.py b/Lib/getpass.py index b81026f..918f2ed 100644 --- a/Lib/getpass.py +++ b/Lib/getpass.py @@ -14,106 +14,105 @@ On the Mac EasyDialogs.AskPassword is used, if available. import sys def unix_getpass(prompt='Password: '): - """Prompt for a password, with echo turned off. + """Prompt for a password, with echo turned off. - Restore terminal settings at end. - """ + Restore terminal settings at end. + """ - try: - fd = sys.stdin.fileno() - except: - return default_getpass(prompt) + try: + fd = sys.stdin.fileno() + except: + return default_getpass(prompt) - getpass = default_getpass - old = termios.tcgetattr(fd) # a copy to save - new = old[:] + getpass = default_getpass + old = termios.tcgetattr(fd) # a copy to save + new = old[:] - new[3] = new[3] & ~TERMIOS.ECHO # 3 == 'lflags' - try: - termios.tcsetattr(fd, TERMIOS.TCSADRAIN, new) - passwd = _raw_input(prompt) - finally: - termios.tcsetattr(fd, TERMIOS.TCSADRAIN, old) + new[3] = new[3] & ~TERMIOS.ECHO # 3 == 'lflags' + try: + termios.tcsetattr(fd, TERMIOS.TCSADRAIN, new) + passwd = _raw_input(prompt) + finally: + termios.tcsetattr(fd, TERMIOS.TCSADRAIN, old) - sys.stdout.write('\n') - return passwd + sys.stdout.write('\n') + return passwd def win_getpass(prompt='Password: '): - """Prompt for password with echo off, using Windows getch().""" - import msvcrt - for c in prompt: - msvcrt.putch(c) - pw = "" - while 1: - c = msvcrt.getch() - if c == '\r' or c == '\n': - break - if c == '\003': - raise KeyboardInterrupt - if c == '\b': - pw = pw[:-1] - else: - pw = pw + c - msvcrt.putch('\r') - msvcrt.putch('\n') - return pw + """Prompt for password with echo off, using Windows getch().""" + import msvcrt + for c in prompt: + msvcrt.putch(c) + pw = "" + while 1: + c = msvcrt.getch() + if c == '\r' or c == '\n': + break + if c == '\003': + raise KeyboardInterrupt + if c == '\b': + pw = pw[:-1] + else: + pw = pw + c + msvcrt.putch('\r') + msvcrt.putch('\n') + return pw def default_getpass(prompt='Password: '): - print "Warning: Problem with getpass. Passwords may be echoed." - return _raw_input(prompt) + print "Warning: Problem with getpass. Passwords may be echoed." + return _raw_input(prompt) def _raw_input(prompt=""): - # A raw_input() replacement that doesn't save the string in the - # GNU readline history. - import sys - prompt = str(prompt) - if prompt: - sys.stdout.write(prompt) - line = sys.stdin.readline() - if not line: - raise EOFError - if line[-1] == '\n': - line = line[:-1] - return line + # A raw_input() replacement that doesn't save the string in the + # GNU readline history. + import sys + prompt = str(prompt) + if prompt: + sys.stdout.write(prompt) + line = sys.stdin.readline() + if not line: + raise EOFError + if line[-1] == '\n': + line = line[:-1] + return line def getuser(): - """Get the username from the environment or password database. + """Get the username from the environment or password database. - First try various environment variables, then the password - database. This works on Windows as long as USERNAME is set. + First try various environment variables, then the password + database. This works on Windows as long as USERNAME is set. - """ + """ - import os + import os - for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): - user = os.environ.get(name) - if user: - return user + for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): + user = os.environ.get(name) + if user: + return user - # If this fails, the exception will "explain" why - import pwd - return pwd.getpwuid(os.getuid())[0] + # If this fails, the exception will "explain" why + import pwd + return pwd.getpwuid(os.getuid())[0] # Bind the name getpass to the appropriate function try: - import termios, TERMIOS + import termios, TERMIOS except ImportError: - try: - import msvcrt - except ImportError: - try: - from EasyDialogs import AskPassword - except ImportError: - getpass = default_getpass - else: - getpass = AskPassword - else: - getpass = win_getpass + try: + import msvcrt + except ImportError: + try: + from EasyDialogs import AskPassword + except ImportError: + getpass = default_getpass + else: + getpass = AskPassword + else: + getpass = win_getpass else: - getpass = unix_getpass - + getpass = unix_getpass diff --git a/Lib/gettext.py b/Lib/gettext.py index 9bebb11..7ef4259 100644 --- a/Lib/gettext.py +++ b/Lib/gettext.py @@ -51,7 +51,7 @@ from errno import ENOENT _default_localedir = os.path.join(sys.prefix, 'share', 'locale') - + def _expand_lang(locale): from locale import normalize locale = normalize(locale) @@ -94,7 +94,7 @@ def _expand_lang(locale): return ret - + class NullTranslations: def __init__(self, fp=None): self._info = {} @@ -192,7 +192,7 @@ class GNUTranslations(NullTranslations): return unicode(tmsg, self._charset) - + # Locate a .mo file using the gettext strategy def find(domain, localedir=None, languages=None): # Get some reasonable defaults for arguments that were not supplied @@ -223,7 +223,7 @@ def find(domain, localedir=None, languages=None): return None - + # a mapping between absolute .mo file path and Translation object _translations = {} @@ -243,12 +243,12 @@ def translation(domain, localedir=None, languages=None, class_=None): return t - + def install(domain, localedir=None, unicode=0): translation(domain, localedir).install(unicode) - + # a mapping b/w domains and locale directories _localedirs = {} # current global domain, `messages' used for compatibility w/ GNU gettext @@ -275,7 +275,7 @@ def dgettext(domain, message): except IOError: return message return t.gettext(message) - + def gettext(message): return dgettext(_current_domain, message) diff --git a/Lib/glob.py b/Lib/glob.py index 599d41b5..2c21455 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -6,51 +6,51 @@ import re def glob(pathname): - """Return a list of paths matching a pathname pattern. - - The pattern may contain simple shell-style wildcards a la fnmatch. - - """ - if not has_magic(pathname): - if os.path.exists(pathname): - return [pathname] - else: - return [] - dirname, basename = os.path.split(pathname) - if has_magic(dirname): - list = glob(dirname) - else: - list = [dirname] - if not has_magic(basename): - result = [] - for dirname in list: - if basename or os.path.isdir(dirname): - name = os.path.join(dirname, basename) - if os.path.exists(name): - result.append(name) - else: - result = [] - for dirname in list: - sublist = glob1(dirname, basename) - for name in sublist: - result.append(os.path.join(dirname, name)) - return result + """Return a list of paths matching a pathname pattern. + + The pattern may contain simple shell-style wildcards a la fnmatch. + + """ + if not has_magic(pathname): + if os.path.exists(pathname): + return [pathname] + else: + return [] + dirname, basename = os.path.split(pathname) + if has_magic(dirname): + list = glob(dirname) + else: + list = [dirname] + if not has_magic(basename): + result = [] + for dirname in list: + if basename or os.path.isdir(dirname): + name = os.path.join(dirname, basename) + if os.path.exists(name): + result.append(name) + else: + result = [] + for dirname in list: + sublist = glob1(dirname, basename) + for name in sublist: + result.append(os.path.join(dirname, name)) + return result def glob1(dirname, pattern): - if not dirname: dirname = os.curdir - try: - names = os.listdir(dirname) - except os.error: - return [] - result = [] - for name in names: - if name[0] != '.' or pattern[0] == '.': - if fnmatch.fnmatch(name, pattern): - result.append(name) - return result + if not dirname: dirname = os.curdir + try: + names = os.listdir(dirname) + except os.error: + return [] + result = [] + for name in names: + if name[0] != '.' or pattern[0] == '.': + if fnmatch.fnmatch(name, pattern): + result.append(name) + return result magic_check = re.compile('[*?[]') def has_magic(s): - return magic_check.search(s) is not None + return magic_check.search(s) is not None diff --git a/Lib/gzip.py b/Lib/gzip.py index 0c9642b..2ed3d9c 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -15,7 +15,7 @@ READ, WRITE = 1, 2 def write32(output, value): output.write(struct.pack(" self.extrasize: size = self.extrasize - + chunk = self.extrabuf[:size] self.extrabuf = self.extrabuf[size:] self.extrasize = self.extrasize - size @@ -171,11 +171,11 @@ class GzipFile: def _read(self, size=1024): if self.fileobj is None: raise EOFError, "Reached EOF" - + if self._new_member: # If the _new_member flag is set, we have to # jump to the next member, if there is one. - # + # # First, check if we're at the end of the file; # if so, it's time to stop; no more members to read. pos = self.fileobj.tell() # Save current position @@ -183,27 +183,27 @@ class GzipFile: if pos == self.fileobj.tell(): self.fileobj = None raise EOFError, "Reached EOF" - else: + else: self.fileobj.seek( pos ) # Return to original position - - self._init_read() + + self._init_read() self._read_gzip_header() self.decompress = zlib.decompressobj(-zlib.MAX_WBITS) self._new_member = 0 - + # Read a chunk of data from the file buf = self.fileobj.read(size) - + # If the EOF has been reached, flush the decompression object # and mark this object as finished. - + if buf == "": uncompress = self.decompress.flush() self._read_eof() self.fileobj = None self._add_read_data( uncompress ) raise EOFError, 'Reached EOF' - + uncompress = self.decompress.decompress(buf) self._add_read_data( uncompress ) @@ -216,11 +216,11 @@ class GzipFile: self.fileobj.seek( -len(self.decompress.unused_data)+8, 1) # Check the CRC and file size, and set the flag so we read - # a new member on the next call + # a new member on the next call self._read_eof() - self._new_member = 1 - - def _add_read_data(self, data): + self._new_member = 1 + + def _add_read_data(self, data): self.crc = zlib.crc32(data, self.crc) self.extrabuf = self.extrabuf + data self.extrasize = self.extrasize + len(data) @@ -228,7 +228,7 @@ class GzipFile: def _read_eof(self): # We've read to the end of the file, so we have to rewind in order - # to reread the 8 bytes containing the CRC and the file size. + # to reread the 8 bytes containing the CRC and the file size. # We check the that the computed CRC and size of the # uncompressed data matches the stored values. self.fileobj.seek(-8, 1) @@ -238,7 +238,7 @@ class GzipFile: raise ValueError, "CRC check failed" elif isize != self.size: raise ValueError, "Incorrect length of data produced" - + def close(self): if self.mode == WRITE: self.fileobj.write(self.compress.flush()) @@ -259,7 +259,7 @@ class GzipFile: except AttributeError: return self.close() - + def flush(self): self.fileobj.flush() @@ -285,7 +285,7 @@ class GzipFile: i = string.find(c, '\n') if size is not None: # We set i=size to break out of the loop under two - # conditions: 1) there's no newline, and the chunk is + # conditions: 1) there's no newline, and the chunk is # larger than size, or 2) there is a newline, but the # resulting line would be longer than 'size'. if i==-1 and len(c) > size: i=size-1 @@ -300,7 +300,7 @@ class GzipFile: bufs.append(c) size = size - len(c) readsize = min(size, readsize * 2) - + def readlines(self, sizehint=0): # Negative numbers result in reading all the lines if sizehint <= 0: sizehint = sys.maxint diff --git a/Lib/htmlentitydefs.py b/Lib/htmlentitydefs.py index 6682bf2..b20a07c 100644 --- a/Lib/htmlentitydefs.py +++ b/Lib/htmlentitydefs.py @@ -1,257 +1,257 @@ """HTML character entity references.""" entitydefs = { - 'AElig': '\306', # latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 - 'Aacute': '\301', # latin capital letter A with acute, U+00C1 ISOlat1 - 'Acirc': '\302', # latin capital letter A with circumflex, U+00C2 ISOlat1 - 'Agrave': '\300', # latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1 - 'Alpha': 'Α', # greek capital letter alpha, U+0391 - 'Aring': '\305', # latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 - 'Atilde': '\303', # latin capital letter A with tilde, U+00C3 ISOlat1 - 'Auml': '\304', # latin capital letter A with diaeresis, U+00C4 ISOlat1 - 'Beta': 'Β', # greek capital letter beta, U+0392 - 'Ccedil': '\307', # latin capital letter C with cedilla, U+00C7 ISOlat1 - 'Chi': 'Χ', # greek capital letter chi, U+03A7 - 'Dagger': '‡', # double dagger, U+2021 ISOpub - 'Delta': 'Δ', # greek capital letter delta, U+0394 ISOgrk3 - 'ETH': '\320', # latin capital letter ETH, U+00D0 ISOlat1 - 'Eacute': '\311', # latin capital letter E with acute, U+00C9 ISOlat1 - 'Ecirc': '\312', # latin capital letter E with circumflex, U+00CA ISOlat1 - 'Egrave': '\310', # latin capital letter E with grave, U+00C8 ISOlat1 - 'Epsilon': 'Ε', # greek capital letter epsilon, U+0395 - 'Eta': 'Η', # greek capital letter eta, U+0397 - 'Euml': '\313', # latin capital letter E with diaeresis, U+00CB ISOlat1 - 'Gamma': 'Γ', # greek capital letter gamma, U+0393 ISOgrk3 - 'Iacute': '\315', # latin capital letter I with acute, U+00CD ISOlat1 - 'Icirc': '\316', # latin capital letter I with circumflex, U+00CE ISOlat1 - 'Igrave': '\314', # latin capital letter I with grave, U+00CC ISOlat1 - 'Iota': 'Ι', # greek capital letter iota, U+0399 - 'Iuml': '\317', # latin capital letter I with diaeresis, U+00CF ISOlat1 - 'Kappa': 'Κ', # greek capital letter kappa, U+039A - 'Lambda': 'Λ', # greek capital letter lambda, U+039B ISOgrk3 - 'Mu': 'Μ', # greek capital letter mu, U+039C - 'Ntilde': '\321', # latin capital letter N with tilde, U+00D1 ISOlat1 - 'Nu': 'Ν', # greek capital letter nu, U+039D - 'OElig': 'Œ', # latin capital ligature OE, U+0152 ISOlat2 - 'Oacute': '\323', # latin capital letter O with acute, U+00D3 ISOlat1 - 'Ocirc': '\324', # latin capital letter O with circumflex, U+00D4 ISOlat1 - 'Ograve': '\322', # latin capital letter O with grave, U+00D2 ISOlat1 - 'Omega': 'Ω', # greek capital letter omega, U+03A9 ISOgrk3 - 'Omicron': 'Ο', # greek capital letter omicron, U+039F - 'Oslash': '\330', # latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 - 'Otilde': '\325', # latin capital letter O with tilde, U+00D5 ISOlat1 - 'Ouml': '\326', # latin capital letter O with diaeresis, U+00D6 ISOlat1 - 'Phi': 'Φ', # greek capital letter phi, U+03A6 ISOgrk3 - 'Pi': 'Π', # greek capital letter pi, U+03A0 ISOgrk3 - 'Prime': '″', # double prime = seconds = inches, U+2033 ISOtech - 'Psi': 'Ψ', # greek capital letter psi, U+03A8 ISOgrk3 - 'Rho': 'Ρ', # greek capital letter rho, U+03A1 - 'Scaron': 'Š', # latin capital letter S with caron, U+0160 ISOlat2 - 'Sigma': 'Σ', # greek capital letter sigma, U+03A3 ISOgrk3 - 'THORN': '\336', # latin capital letter THORN, U+00DE ISOlat1 - 'Tau': 'Τ', # greek capital letter tau, U+03A4 - 'Theta': 'Θ', # greek capital letter theta, U+0398 ISOgrk3 - 'Uacute': '\332', # latin capital letter U with acute, U+00DA ISOlat1 - 'Ucirc': '\333', # latin capital letter U with circumflex, U+00DB ISOlat1 - 'Ugrave': '\331', # latin capital letter U with grave, U+00D9 ISOlat1 - 'Upsilon': 'Υ', # greek capital letter upsilon, U+03A5 ISOgrk3 - 'Uuml': '\334', # latin capital letter U with diaeresis, U+00DC ISOlat1 - 'Xi': 'Ξ', # greek capital letter xi, U+039E ISOgrk3 - 'Yacute': '\335', # latin capital letter Y with acute, U+00DD ISOlat1 - 'Yuml': 'Ÿ', # latin capital letter Y with diaeresis, U+0178 ISOlat2 - 'Zeta': 'Ζ', # greek capital letter zeta, U+0396 - 'aacute': '\341', # latin small letter a with acute, U+00E1 ISOlat1 - 'acirc': '\342', # latin small letter a with circumflex, U+00E2 ISOlat1 - 'acute': '\264', # acute accent = spacing acute, U+00B4 ISOdia - 'aelig': '\346', # latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 - 'agrave': '\340', # latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 - 'alefsym': 'ℵ', # alef symbol = first transfinite cardinal, U+2135 NEW - 'alpha': 'α', # greek small letter alpha, U+03B1 ISOgrk3 - 'amp': '\46', # ampersand, U+0026 ISOnum - 'and': '∧', # logical and = wedge, U+2227 ISOtech - 'ang': '∠', # angle, U+2220 ISOamso - 'aring': '\345', # latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 - 'asymp': '≈', # almost equal to = asymptotic to, U+2248 ISOamsr - 'atilde': '\343', # latin small letter a with tilde, U+00E3 ISOlat1 - 'auml': '\344', # latin small letter a with diaeresis, U+00E4 ISOlat1 - 'bdquo': '„', # double low-9 quotation mark, U+201E NEW - 'beta': 'β', # greek small letter beta, U+03B2 ISOgrk3 - 'brvbar': '\246', # broken bar = broken vertical bar, U+00A6 ISOnum - 'bull': '•', # bullet = black small circle, U+2022 ISOpub - 'cap': '∩', # intersection = cap, U+2229 ISOtech - 'ccedil': '\347', # latin small letter c with cedilla, U+00E7 ISOlat1 - 'cedil': '\270', # cedilla = spacing cedilla, U+00B8 ISOdia - 'cent': '\242', # cent sign, U+00A2 ISOnum - 'chi': 'χ', # greek small letter chi, U+03C7 ISOgrk3 - 'circ': 'ˆ', # modifier letter circumflex accent, U+02C6 ISOpub - 'clubs': '♣', # black club suit = shamrock, U+2663 ISOpub - 'cong': '≅', # approximately equal to, U+2245 ISOtech - 'copy': '\251', # copyright sign, U+00A9 ISOnum - 'crarr': '↵', # downwards arrow with corner leftwards = carriage return, U+21B5 NEW - 'cup': '∪', # union = cup, U+222A ISOtech - 'curren': '\244', # currency sign, U+00A4 ISOnum - 'dArr': '⇓', # downwards double arrow, U+21D3 ISOamsa - 'dagger': '†', # dagger, U+2020 ISOpub - 'darr': '↓', # downwards arrow, U+2193 ISOnum - 'deg': '\260', # degree sign, U+00B0 ISOnum - 'delta': 'δ', # greek small letter delta, U+03B4 ISOgrk3 - 'diams': '♦', # black diamond suit, U+2666 ISOpub - 'divide': '\367', # division sign, U+00F7 ISOnum - 'eacute': '\351', # latin small letter e with acute, U+00E9 ISOlat1 - 'ecirc': '\352', # latin small letter e with circumflex, U+00EA ISOlat1 - 'egrave': '\350', # latin small letter e with grave, U+00E8 ISOlat1 - 'empty': '∅', # empty set = null set = diameter, U+2205 ISOamso - 'emsp': ' ', # em space, U+2003 ISOpub - 'ensp': ' ', # en space, U+2002 ISOpub - 'epsilon': 'ε', # greek small letter epsilon, U+03B5 ISOgrk3 - 'equiv': '≡', # identical to, U+2261 ISOtech - 'eta': 'η', # greek small letter eta, U+03B7 ISOgrk3 - 'eth': '\360', # latin small letter eth, U+00F0 ISOlat1 - 'euml': '\353', # latin small letter e with diaeresis, U+00EB ISOlat1 - 'euro': '€', # euro sign, U+20AC NEW - 'exist': '∃', # there exists, U+2203 ISOtech - 'fnof': 'ƒ', # latin small f with hook = function = florin, U+0192 ISOtech - 'forall': '∀', # for all, U+2200 ISOtech - 'frac12': '\275', # vulgar fraction one half = fraction one half, U+00BD ISOnum - 'frac14': '\274', # vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum - 'frac34': '\276', # vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum - 'frasl': '⁄', # fraction slash, U+2044 NEW - 'gamma': 'γ', # greek small letter gamma, U+03B3 ISOgrk3 - 'ge': '≥', # greater-than or equal to, U+2265 ISOtech - 'gt': '\76', # greater-than sign, U+003E ISOnum - 'hArr': '⇔', # left right double arrow, U+21D4 ISOamsa - 'harr': '↔', # left right arrow, U+2194 ISOamsa - 'hearts': '♥', # black heart suit = valentine, U+2665 ISOpub - 'hellip': '…', # horizontal ellipsis = three dot leader, U+2026 ISOpub - 'iacute': '\355', # latin small letter i with acute, U+00ED ISOlat1 - 'icirc': '\356', # latin small letter i with circumflex, U+00EE ISOlat1 - 'iexcl': '\241', # inverted exclamation mark, U+00A1 ISOnum - 'igrave': '\354', # latin small letter i with grave, U+00EC ISOlat1 - 'image': 'ℑ', # blackletter capital I = imaginary part, U+2111 ISOamso - 'infin': '∞', # infinity, U+221E ISOtech - 'int': '∫', # integral, U+222B ISOtech - 'iota': 'ι', # greek small letter iota, U+03B9 ISOgrk3 - 'iquest': '\277', # inverted question mark = turned question mark, U+00BF ISOnum - 'isin': '∈', # element of, U+2208 ISOtech - 'iuml': '\357', # latin small letter i with diaeresis, U+00EF ISOlat1 - 'kappa': 'κ', # greek small letter kappa, U+03BA ISOgrk3 - 'lArr': '⇐', # leftwards double arrow, U+21D0 ISOtech - 'lambda': 'λ', # greek small letter lambda, U+03BB ISOgrk3 - 'lang': '〈', # left-pointing angle bracket = bra, U+2329 ISOtech - 'laquo': '\253', # left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum - 'larr': '←', # leftwards arrow, U+2190 ISOnum - 'lceil': '⌈', # left ceiling = apl upstile, U+2308 ISOamsc - 'ldquo': '“', # left double quotation mark, U+201C ISOnum - 'le': '≤', # less-than or equal to, U+2264 ISOtech - 'lfloor': '⌊', # left floor = apl downstile, U+230A ISOamsc - 'lowast': '∗', # asterisk operator, U+2217 ISOtech - 'loz': '◊', # lozenge, U+25CA ISOpub - 'lrm': '‎', # left-to-right mark, U+200E NEW RFC 2070 - 'lsaquo': '‹', # single left-pointing angle quotation mark, U+2039 ISO proposed - 'lsquo': '‘', # left single quotation mark, U+2018 ISOnum - 'lt': '\74', # less-than sign, U+003C ISOnum - 'macr': '\257', # macron = spacing macron = overline = APL overbar, U+00AF ISOdia - 'mdash': '—', # em dash, U+2014 ISOpub - 'micro': '\265', # micro sign, U+00B5 ISOnum - 'middot': '\267', # middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum - 'minus': '−', # minus sign, U+2212 ISOtech - 'mu': 'μ', # greek small letter mu, U+03BC ISOgrk3 - 'nabla': '∇', # nabla = backward difference, U+2207 ISOtech - 'nbsp': '\240', # no-break space = non-breaking space, U+00A0 ISOnum - 'ndash': '–', # en dash, U+2013 ISOpub - 'ne': '≠', # not equal to, U+2260 ISOtech - 'ni': '∋', # contains as member, U+220B ISOtech - 'not': '\254', # not sign, U+00AC ISOnum - 'notin': '∉', # not an element of, U+2209 ISOtech - 'nsub': '⊄', # not a subset of, U+2284 ISOamsn - 'ntilde': '\361', # latin small letter n with tilde, U+00F1 ISOlat1 - 'nu': 'ν', # greek small letter nu, U+03BD ISOgrk3 - 'oacute': '\363', # latin small letter o with acute, U+00F3 ISOlat1 - 'ocirc': '\364', # latin small letter o with circumflex, U+00F4 ISOlat1 - 'oelig': 'œ', # latin small ligature oe, U+0153 ISOlat2 - 'ograve': '\362', # latin small letter o with grave, U+00F2 ISOlat1 - 'oline': '‾', # overline = spacing overscore, U+203E NEW - 'omega': 'ω', # greek small letter omega, U+03C9 ISOgrk3 - 'omicron': 'ο', # greek small letter omicron, U+03BF NEW - 'oplus': '⊕', # circled plus = direct sum, U+2295 ISOamsb - 'or': '∨', # logical or = vee, U+2228 ISOtech - 'ordf': '\252', # feminine ordinal indicator, U+00AA ISOnum - 'ordm': '\272', # masculine ordinal indicator, U+00BA ISOnum - 'oslash': '\370', # latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 - 'otilde': '\365', # latin small letter o with tilde, U+00F5 ISOlat1 - 'otimes': '⊗', # circled times = vector product, U+2297 ISOamsb - 'ouml': '\366', # latin small letter o with diaeresis, U+00F6 ISOlat1 - 'para': '\266', # pilcrow sign = paragraph sign, U+00B6 ISOnum - 'part': '∂', # partial differential, U+2202 ISOtech - 'permil': '‰', # per mille sign, U+2030 ISOtech - 'perp': '⊥', # up tack = orthogonal to = perpendicular, U+22A5 ISOtech - 'phi': 'φ', # greek small letter phi, U+03C6 ISOgrk3 - 'pi': 'π', # greek small letter pi, U+03C0 ISOgrk3 - 'piv': 'ϖ', # greek pi symbol, U+03D6 ISOgrk3 - 'plusmn': '\261', # plus-minus sign = plus-or-minus sign, U+00B1 ISOnum - 'pound': '\243', # pound sign, U+00A3 ISOnum - 'prime': '′', # prime = minutes = feet, U+2032 ISOtech - 'prod': '∏', # n-ary product = product sign, U+220F ISOamsb - 'prop': '∝', # proportional to, U+221D ISOtech - 'psi': 'ψ', # greek small letter psi, U+03C8 ISOgrk3 - 'quot': '\42', # quotation mark = APL quote, U+0022 ISOnum - 'rArr': '⇒', # rightwards double arrow, U+21D2 ISOtech - 'radic': '√', # square root = radical sign, U+221A ISOtech - 'rang': '〉', # right-pointing angle bracket = ket, U+232A ISOtech - 'raquo': '\273', # right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum - 'rarr': '→', # rightwards arrow, U+2192 ISOnum - 'rceil': '⌉', # right ceiling, U+2309 ISOamsc - 'rdquo': '”', # right double quotation mark, U+201D ISOnum - 'real': 'ℜ', # blackletter capital R = real part symbol, U+211C ISOamso - 'reg': '\256', # registered sign = registered trade mark sign, U+00AE ISOnum - 'rfloor': '⌋', # right floor, U+230B ISOamsc - 'rho': 'ρ', # greek small letter rho, U+03C1 ISOgrk3 - 'rlm': '‏', # right-to-left mark, U+200F NEW RFC 2070 - 'rsaquo': '›', # single right-pointing angle quotation mark, U+203A ISO proposed - 'rsquo': '’', # right single quotation mark, U+2019 ISOnum - 'sbquo': '‚', # single low-9 quotation mark, U+201A NEW - 'scaron': 'š', # latin small letter s with caron, U+0161 ISOlat2 - 'sdot': '⋅', # dot operator, U+22C5 ISOamsb - 'sect': '\247', # section sign, U+00A7 ISOnum - 'shy': '\255', # soft hyphen = discretionary hyphen, U+00AD ISOnum - 'sigma': 'σ', # greek small letter sigma, U+03C3 ISOgrk3 - 'sigmaf': 'ς', # greek small letter final sigma, U+03C2 ISOgrk3 - 'sim': '∼', # tilde operator = varies with = similar to, U+223C ISOtech - 'spades': '♠', # black spade suit, U+2660 ISOpub - 'sub': '⊂', # subset of, U+2282 ISOtech - 'sube': '⊆', # subset of or equal to, U+2286 ISOtech - 'sum': '∑', # n-ary sumation, U+2211 ISOamsb - 'sup': '⊃', # superset of, U+2283 ISOtech - 'sup1': '\271', # superscript one = superscript digit one, U+00B9 ISOnum - 'sup2': '\262', # superscript two = superscript digit two = squared, U+00B2 ISOnum - 'sup3': '\263', # superscript three = superscript digit three = cubed, U+00B3 ISOnum - 'supe': '⊇', # superset of or equal to, U+2287 ISOtech - 'szlig': '\337', # latin small letter sharp s = ess-zed, U+00DF ISOlat1 - 'tau': 'τ', # greek small letter tau, U+03C4 ISOgrk3 - 'there4': '∴', # therefore, U+2234 ISOtech - 'theta': 'θ', # greek small letter theta, U+03B8 ISOgrk3 - 'thetasym': 'ϑ', # greek small letter theta symbol, U+03D1 NEW - 'thinsp': ' ', # thin space, U+2009 ISOpub - 'thorn': '\376', # latin small letter thorn with, U+00FE ISOlat1 - 'tilde': '˜', # small tilde, U+02DC ISOdia - 'times': '\327', # multiplication sign, U+00D7 ISOnum - 'trade': '™', # trade mark sign, U+2122 ISOnum - 'uArr': '⇑', # upwards double arrow, U+21D1 ISOamsa - 'uacute': '\372', # latin small letter u with acute, U+00FA ISOlat1 - 'uarr': '↑', # upwards arrow, U+2191 ISOnum - 'ucirc': '\373', # latin small letter u with circumflex, U+00FB ISOlat1 - 'ugrave': '\371', # latin small letter u with grave, U+00F9 ISOlat1 - 'uml': '\250', # diaeresis = spacing diaeresis, U+00A8 ISOdia - 'upsih': 'ϒ', # greek upsilon with hook symbol, U+03D2 NEW - 'upsilon': 'υ', # greek small letter upsilon, U+03C5 ISOgrk3 - 'uuml': '\374', # latin small letter u with diaeresis, U+00FC ISOlat1 - 'weierp': '℘', # script capital P = power set = Weierstrass p, U+2118 ISOamso - 'xi': 'ξ', # greek small letter xi, U+03BE ISOgrk3 - 'yacute': '\375', # latin small letter y with acute, U+00FD ISOlat1 - 'yen': '\245', # yen sign = yuan sign, U+00A5 ISOnum - 'yuml': '\377', # latin small letter y with diaeresis, U+00FF ISOlat1 - 'zeta': 'ζ', # greek small letter zeta, U+03B6 ISOgrk3 - 'zwj': '‍', # zero width joiner, U+200D NEW RFC 2070 - 'zwnj': '‌', # zero width non-joiner, U+200C NEW RFC 2070 + 'AElig': '\306', # latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 + 'Aacute': '\301', # latin capital letter A with acute, U+00C1 ISOlat1 + 'Acirc': '\302', # latin capital letter A with circumflex, U+00C2 ISOlat1 + 'Agrave': '\300', # latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1 + 'Alpha': 'Α', # greek capital letter alpha, U+0391 + 'Aring': '\305', # latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 + 'Atilde': '\303', # latin capital letter A with tilde, U+00C3 ISOlat1 + 'Auml': '\304', # latin capital letter A with diaeresis, U+00C4 ISOlat1 + 'Beta': 'Β', # greek capital letter beta, U+0392 + 'Ccedil': '\307', # latin capital letter C with cedilla, U+00C7 ISOlat1 + 'Chi': 'Χ', # greek capital letter chi, U+03A7 + 'Dagger': '‡', # double dagger, U+2021 ISOpub + 'Delta': 'Δ', # greek capital letter delta, U+0394 ISOgrk3 + 'ETH': '\320', # latin capital letter ETH, U+00D0 ISOlat1 + 'Eacute': '\311', # latin capital letter E with acute, U+00C9 ISOlat1 + 'Ecirc': '\312', # latin capital letter E with circumflex, U+00CA ISOlat1 + 'Egrave': '\310', # latin capital letter E with grave, U+00C8 ISOlat1 + 'Epsilon': 'Ε', # greek capital letter epsilon, U+0395 + 'Eta': 'Η', # greek capital letter eta, U+0397 + 'Euml': '\313', # latin capital letter E with diaeresis, U+00CB ISOlat1 + 'Gamma': 'Γ', # greek capital letter gamma, U+0393 ISOgrk3 + 'Iacute': '\315', # latin capital letter I with acute, U+00CD ISOlat1 + 'Icirc': '\316', # latin capital letter I with circumflex, U+00CE ISOlat1 + 'Igrave': '\314', # latin capital letter I with grave, U+00CC ISOlat1 + 'Iota': 'Ι', # greek capital letter iota, U+0399 + 'Iuml': '\317', # latin capital letter I with diaeresis, U+00CF ISOlat1 + 'Kappa': 'Κ', # greek capital letter kappa, U+039A + 'Lambda': 'Λ', # greek capital letter lambda, U+039B ISOgrk3 + 'Mu': 'Μ', # greek capital letter mu, U+039C + 'Ntilde': '\321', # latin capital letter N with tilde, U+00D1 ISOlat1 + 'Nu': 'Ν', # greek capital letter nu, U+039D + 'OElig': 'Œ', # latin capital ligature OE, U+0152 ISOlat2 + 'Oacute': '\323', # latin capital letter O with acute, U+00D3 ISOlat1 + 'Ocirc': '\324', # latin capital letter O with circumflex, U+00D4 ISOlat1 + 'Ograve': '\322', # latin capital letter O with grave, U+00D2 ISOlat1 + 'Omega': 'Ω', # greek capital letter omega, U+03A9 ISOgrk3 + 'Omicron': 'Ο', # greek capital letter omicron, U+039F + 'Oslash': '\330', # latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 + 'Otilde': '\325', # latin capital letter O with tilde, U+00D5 ISOlat1 + 'Ouml': '\326', # latin capital letter O with diaeresis, U+00D6 ISOlat1 + 'Phi': 'Φ', # greek capital letter phi, U+03A6 ISOgrk3 + 'Pi': 'Π', # greek capital letter pi, U+03A0 ISOgrk3 + 'Prime': '″', # double prime = seconds = inches, U+2033 ISOtech + 'Psi': 'Ψ', # greek capital letter psi, U+03A8 ISOgrk3 + 'Rho': 'Ρ', # greek capital letter rho, U+03A1 + 'Scaron': 'Š', # latin capital letter S with caron, U+0160 ISOlat2 + 'Sigma': 'Σ', # greek capital letter sigma, U+03A3 ISOgrk3 + 'THORN': '\336', # latin capital letter THORN, U+00DE ISOlat1 + 'Tau': 'Τ', # greek capital letter tau, U+03A4 + 'Theta': 'Θ', # greek capital letter theta, U+0398 ISOgrk3 + 'Uacute': '\332', # latin capital letter U with acute, U+00DA ISOlat1 + 'Ucirc': '\333', # latin capital letter U with circumflex, U+00DB ISOlat1 + 'Ugrave': '\331', # latin capital letter U with grave, U+00D9 ISOlat1 + 'Upsilon': 'Υ', # greek capital letter upsilon, U+03A5 ISOgrk3 + 'Uuml': '\334', # latin capital letter U with diaeresis, U+00DC ISOlat1 + 'Xi': 'Ξ', # greek capital letter xi, U+039E ISOgrk3 + 'Yacute': '\335', # latin capital letter Y with acute, U+00DD ISOlat1 + 'Yuml': 'Ÿ', # latin capital letter Y with diaeresis, U+0178 ISOlat2 + 'Zeta': 'Ζ', # greek capital letter zeta, U+0396 + 'aacute': '\341', # latin small letter a with acute, U+00E1 ISOlat1 + 'acirc': '\342', # latin small letter a with circumflex, U+00E2 ISOlat1 + 'acute': '\264', # acute accent = spacing acute, U+00B4 ISOdia + 'aelig': '\346', # latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 + 'agrave': '\340', # latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 + 'alefsym': 'ℵ', # alef symbol = first transfinite cardinal, U+2135 NEW + 'alpha': 'α', # greek small letter alpha, U+03B1 ISOgrk3 + 'amp': '\46', # ampersand, U+0026 ISOnum + 'and': '∧', # logical and = wedge, U+2227 ISOtech + 'ang': '∠', # angle, U+2220 ISOamso + 'aring': '\345', # latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 + 'asymp': '≈', # almost equal to = asymptotic to, U+2248 ISOamsr + 'atilde': '\343', # latin small letter a with tilde, U+00E3 ISOlat1 + 'auml': '\344', # latin small letter a with diaeresis, U+00E4 ISOlat1 + 'bdquo': '„', # double low-9 quotation mark, U+201E NEW + 'beta': 'β', # greek small letter beta, U+03B2 ISOgrk3 + 'brvbar': '\246', # broken bar = broken vertical bar, U+00A6 ISOnum + 'bull': '•', # bullet = black small circle, U+2022 ISOpub + 'cap': '∩', # intersection = cap, U+2229 ISOtech + 'ccedil': '\347', # latin small letter c with cedilla, U+00E7 ISOlat1 + 'cedil': '\270', # cedilla = spacing cedilla, U+00B8 ISOdia + 'cent': '\242', # cent sign, U+00A2 ISOnum + 'chi': 'χ', # greek small letter chi, U+03C7 ISOgrk3 + 'circ': 'ˆ', # modifier letter circumflex accent, U+02C6 ISOpub + 'clubs': '♣', # black club suit = shamrock, U+2663 ISOpub + 'cong': '≅', # approximately equal to, U+2245 ISOtech + 'copy': '\251', # copyright sign, U+00A9 ISOnum + 'crarr': '↵', # downwards arrow with corner leftwards = carriage return, U+21B5 NEW + 'cup': '∪', # union = cup, U+222A ISOtech + 'curren': '\244', # currency sign, U+00A4 ISOnum + 'dArr': '⇓', # downwards double arrow, U+21D3 ISOamsa + 'dagger': '†', # dagger, U+2020 ISOpub + 'darr': '↓', # downwards arrow, U+2193 ISOnum + 'deg': '\260', # degree sign, U+00B0 ISOnum + 'delta': 'δ', # greek small letter delta, U+03B4 ISOgrk3 + 'diams': '♦', # black diamond suit, U+2666 ISOpub + 'divide': '\367', # division sign, U+00F7 ISOnum + 'eacute': '\351', # latin small letter e with acute, U+00E9 ISOlat1 + 'ecirc': '\352', # latin small letter e with circumflex, U+00EA ISOlat1 + 'egrave': '\350', # latin small letter e with grave, U+00E8 ISOlat1 + 'empty': '∅', # empty set = null set = diameter, U+2205 ISOamso + 'emsp': ' ', # em space, U+2003 ISOpub + 'ensp': ' ', # en space, U+2002 ISOpub + 'epsilon': 'ε', # greek small letter epsilon, U+03B5 ISOgrk3 + 'equiv': '≡', # identical to, U+2261 ISOtech + 'eta': 'η', # greek small letter eta, U+03B7 ISOgrk3 + 'eth': '\360', # latin small letter eth, U+00F0 ISOlat1 + 'euml': '\353', # latin small letter e with diaeresis, U+00EB ISOlat1 + 'euro': '€', # euro sign, U+20AC NEW + 'exist': '∃', # there exists, U+2203 ISOtech + 'fnof': 'ƒ', # latin small f with hook = function = florin, U+0192 ISOtech + 'forall': '∀', # for all, U+2200 ISOtech + 'frac12': '\275', # vulgar fraction one half = fraction one half, U+00BD ISOnum + 'frac14': '\274', # vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum + 'frac34': '\276', # vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum + 'frasl': '⁄', # fraction slash, U+2044 NEW + 'gamma': 'γ', # greek small letter gamma, U+03B3 ISOgrk3 + 'ge': '≥', # greater-than or equal to, U+2265 ISOtech + 'gt': '\76', # greater-than sign, U+003E ISOnum + 'hArr': '⇔', # left right double arrow, U+21D4 ISOamsa + 'harr': '↔', # left right arrow, U+2194 ISOamsa + 'hearts': '♥', # black heart suit = valentine, U+2665 ISOpub + 'hellip': '…', # horizontal ellipsis = three dot leader, U+2026 ISOpub + 'iacute': '\355', # latin small letter i with acute, U+00ED ISOlat1 + 'icirc': '\356', # latin small letter i with circumflex, U+00EE ISOlat1 + 'iexcl': '\241', # inverted exclamation mark, U+00A1 ISOnum + 'igrave': '\354', # latin small letter i with grave, U+00EC ISOlat1 + 'image': 'ℑ', # blackletter capital I = imaginary part, U+2111 ISOamso + 'infin': '∞', # infinity, U+221E ISOtech + 'int': '∫', # integral, U+222B ISOtech + 'iota': 'ι', # greek small letter iota, U+03B9 ISOgrk3 + 'iquest': '\277', # inverted question mark = turned question mark, U+00BF ISOnum + 'isin': '∈', # element of, U+2208 ISOtech + 'iuml': '\357', # latin small letter i with diaeresis, U+00EF ISOlat1 + 'kappa': 'κ', # greek small letter kappa, U+03BA ISOgrk3 + 'lArr': '⇐', # leftwards double arrow, U+21D0 ISOtech + 'lambda': 'λ', # greek small letter lambda, U+03BB ISOgrk3 + 'lang': '〈', # left-pointing angle bracket = bra, U+2329 ISOtech + 'laquo': '\253', # left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum + 'larr': '←', # leftwards arrow, U+2190 ISOnum + 'lceil': '⌈', # left ceiling = apl upstile, U+2308 ISOamsc + 'ldquo': '“', # left double quotation mark, U+201C ISOnum + 'le': '≤', # less-than or equal to, U+2264 ISOtech + 'lfloor': '⌊', # left floor = apl downstile, U+230A ISOamsc + 'lowast': '∗', # asterisk operator, U+2217 ISOtech + 'loz': '◊', # lozenge, U+25CA ISOpub + 'lrm': '‎', # left-to-right mark, U+200E NEW RFC 2070 + 'lsaquo': '‹', # single left-pointing angle quotation mark, U+2039 ISO proposed + 'lsquo': '‘', # left single quotation mark, U+2018 ISOnum + 'lt': '\74', # less-than sign, U+003C ISOnum + 'macr': '\257', # macron = spacing macron = overline = APL overbar, U+00AF ISOdia + 'mdash': '—', # em dash, U+2014 ISOpub + 'micro': '\265', # micro sign, U+00B5 ISOnum + 'middot': '\267', # middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum + 'minus': '−', # minus sign, U+2212 ISOtech + 'mu': 'μ', # greek small letter mu, U+03BC ISOgrk3 + 'nabla': '∇', # nabla = backward difference, U+2207 ISOtech + 'nbsp': '\240', # no-break space = non-breaking space, U+00A0 ISOnum + 'ndash': '–', # en dash, U+2013 ISOpub + 'ne': '≠', # not equal to, U+2260 ISOtech + 'ni': '∋', # contains as member, U+220B ISOtech + 'not': '\254', # not sign, U+00AC ISOnum + 'notin': '∉', # not an element of, U+2209 ISOtech + 'nsub': '⊄', # not a subset of, U+2284 ISOamsn + 'ntilde': '\361', # latin small letter n with tilde, U+00F1 ISOlat1 + 'nu': 'ν', # greek small letter nu, U+03BD ISOgrk3 + 'oacute': '\363', # latin small letter o with acute, U+00F3 ISOlat1 + 'ocirc': '\364', # latin small letter o with circumflex, U+00F4 ISOlat1 + 'oelig': 'œ', # latin small ligature oe, U+0153 ISOlat2 + 'ograve': '\362', # latin small letter o with grave, U+00F2 ISOlat1 + 'oline': '‾', # overline = spacing overscore, U+203E NEW + 'omega': 'ω', # greek small letter omega, U+03C9 ISOgrk3 + 'omicron': 'ο', # greek small letter omicron, U+03BF NEW + 'oplus': '⊕', # circled plus = direct sum, U+2295 ISOamsb + 'or': '∨', # logical or = vee, U+2228 ISOtech + 'ordf': '\252', # feminine ordinal indicator, U+00AA ISOnum + 'ordm': '\272', # masculine ordinal indicator, U+00BA ISOnum + 'oslash': '\370', # latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 + 'otilde': '\365', # latin small letter o with tilde, U+00F5 ISOlat1 + 'otimes': '⊗', # circled times = vector product, U+2297 ISOamsb + 'ouml': '\366', # latin small letter o with diaeresis, U+00F6 ISOlat1 + 'para': '\266', # pilcrow sign = paragraph sign, U+00B6 ISOnum + 'part': '∂', # partial differential, U+2202 ISOtech + 'permil': '‰', # per mille sign, U+2030 ISOtech + 'perp': '⊥', # up tack = orthogonal to = perpendicular, U+22A5 ISOtech + 'phi': 'φ', # greek small letter phi, U+03C6 ISOgrk3 + 'pi': 'π', # greek small letter pi, U+03C0 ISOgrk3 + 'piv': 'ϖ', # greek pi symbol, U+03D6 ISOgrk3 + 'plusmn': '\261', # plus-minus sign = plus-or-minus sign, U+00B1 ISOnum + 'pound': '\243', # pound sign, U+00A3 ISOnum + 'prime': '′', # prime = minutes = feet, U+2032 ISOtech + 'prod': '∏', # n-ary product = product sign, U+220F ISOamsb + 'prop': '∝', # proportional to, U+221D ISOtech + 'psi': 'ψ', # greek small letter psi, U+03C8 ISOgrk3 + 'quot': '\42', # quotation mark = APL quote, U+0022 ISOnum + 'rArr': '⇒', # rightwards double arrow, U+21D2 ISOtech + 'radic': '√', # square root = radical sign, U+221A ISOtech + 'rang': '〉', # right-pointing angle bracket = ket, U+232A ISOtech + 'raquo': '\273', # right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum + 'rarr': '→', # rightwards arrow, U+2192 ISOnum + 'rceil': '⌉', # right ceiling, U+2309 ISOamsc + 'rdquo': '”', # right double quotation mark, U+201D ISOnum + 'real': 'ℜ', # blackletter capital R = real part symbol, U+211C ISOamso + 'reg': '\256', # registered sign = registered trade mark sign, U+00AE ISOnum + 'rfloor': '⌋', # right floor, U+230B ISOamsc + 'rho': 'ρ', # greek small letter rho, U+03C1 ISOgrk3 + 'rlm': '‏', # right-to-left mark, U+200F NEW RFC 2070 + 'rsaquo': '›', # single right-pointing angle quotation mark, U+203A ISO proposed + 'rsquo': '’', # right single quotation mark, U+2019 ISOnum + 'sbquo': '‚', # single low-9 quotation mark, U+201A NEW + 'scaron': 'š', # latin small letter s with caron, U+0161 ISOlat2 + 'sdot': '⋅', # dot operator, U+22C5 ISOamsb + 'sect': '\247', # section sign, U+00A7 ISOnum + 'shy': '\255', # soft hyphen = discretionary hyphen, U+00AD ISOnum + 'sigma': 'σ', # greek small letter sigma, U+03C3 ISOgrk3 + 'sigmaf': 'ς', # greek small letter final sigma, U+03C2 ISOgrk3 + 'sim': '∼', # tilde operator = varies with = similar to, U+223C ISOtech + 'spades': '♠', # black spade suit, U+2660 ISOpub + 'sub': '⊂', # subset of, U+2282 ISOtech + 'sube': '⊆', # subset of or equal to, U+2286 ISOtech + 'sum': '∑', # n-ary sumation, U+2211 ISOamsb + 'sup': '⊃', # superset of, U+2283 ISOtech + 'sup1': '\271', # superscript one = superscript digit one, U+00B9 ISOnum + 'sup2': '\262', # superscript two = superscript digit two = squared, U+00B2 ISOnum + 'sup3': '\263', # superscript three = superscript digit three = cubed, U+00B3 ISOnum + 'supe': '⊇', # superset of or equal to, U+2287 ISOtech + 'szlig': '\337', # latin small letter sharp s = ess-zed, U+00DF ISOlat1 + 'tau': 'τ', # greek small letter tau, U+03C4 ISOgrk3 + 'there4': '∴', # therefore, U+2234 ISOtech + 'theta': 'θ', # greek small letter theta, U+03B8 ISOgrk3 + 'thetasym': 'ϑ', # greek small letter theta symbol, U+03D1 NEW + 'thinsp': ' ', # thin space, U+2009 ISOpub + 'thorn': '\376', # latin small letter thorn with, U+00FE ISOlat1 + 'tilde': '˜', # small tilde, U+02DC ISOdia + 'times': '\327', # multiplication sign, U+00D7 ISOnum + 'trade': '™', # trade mark sign, U+2122 ISOnum + 'uArr': '⇑', # upwards double arrow, U+21D1 ISOamsa + 'uacute': '\372', # latin small letter u with acute, U+00FA ISOlat1 + 'uarr': '↑', # upwards arrow, U+2191 ISOnum + 'ucirc': '\373', # latin small letter u with circumflex, U+00FB ISOlat1 + 'ugrave': '\371', # latin small letter u with grave, U+00F9 ISOlat1 + 'uml': '\250', # diaeresis = spacing diaeresis, U+00A8 ISOdia + 'upsih': 'ϒ', # greek upsilon with hook symbol, U+03D2 NEW + 'upsilon': 'υ', # greek small letter upsilon, U+03C5 ISOgrk3 + 'uuml': '\374', # latin small letter u with diaeresis, U+00FC ISOlat1 + 'weierp': '℘', # script capital P = power set = Weierstrass p, U+2118 ISOamso + 'xi': 'ξ', # greek small letter xi, U+03BE ISOgrk3 + 'yacute': '\375', # latin small letter y with acute, U+00FD ISOlat1 + 'yen': '\245', # yen sign = yuan sign, U+00A5 ISOnum + 'yuml': '\377', # latin small letter y with diaeresis, U+00FF ISOlat1 + 'zeta': 'ζ', # greek small letter zeta, U+03B6 ISOgrk3 + 'zwj': '‍', # zero width joiner, U+200D NEW RFC 2070 + 'zwnj': '‌', # zero width non-joiner, U+200C NEW RFC 2070 } diff --git a/Lib/htmllib.py b/Lib/htmllib.py index 9cf962c..cae9ae5 100644 --- a/Lib/htmllib.py +++ b/Lib/htmllib.py @@ -411,7 +411,7 @@ def test(args = None): if f is not sys.stdin: f.close() - + if silent: f = formatter.NullFormatter() else: diff --git a/Lib/httplib.py b/Lib/httplib.py index 79789bb..3d3164e 100644 --- a/Lib/httplib.py +++ b/Lib/httplib.py @@ -93,14 +93,14 @@ class HTTPResponse: self.msg = None # from the Status-Line of the response - self.version = _UNKNOWN # HTTP-Version - self.status = _UNKNOWN # Status-Code - self.reason = _UNKNOWN # Reason-Phrase + self.version = _UNKNOWN # HTTP-Version + self.status = _UNKNOWN # Status-Code + self.reason = _UNKNOWN # Reason-Phrase - self.chunked = _UNKNOWN # is "chunked" being used? - self.chunk_left = _UNKNOWN # bytes left to read in current chunk - self.length = _UNKNOWN # number of bytes left in response - self.will_close = _UNKNOWN # conn will close at end of response + self.chunked = _UNKNOWN # is "chunked" being used? + self.chunk_left = _UNKNOWN # bytes left to read in current chunk + self.length = _UNKNOWN # number of bytes left in response + self.will_close = _UNKNOWN # conn will close at end of response def begin(self): if self.msg is not None: @@ -130,7 +130,7 @@ class HTTPResponse: if version == 'HTTP/1.0': self.version = 10 elif version.startswith('HTTP/1.'): - self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1 + self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1 elif version == 'HTTP/0.9': self.version = 9 else: @@ -186,9 +186,9 @@ class HTTPResponse: self.length = None # does the body have a fixed length? (of zero) - if (status == 204 or # No Content - status == 304 or # Not Modified - 100 <= status < 200): # 1xx codes + if (status == 204 or # No Content + status == 304 or # Not Modified + 100 <= status < 200): # 1xx codes self.length = 0 # if the connection remains open, and we aren't using chunked, and @@ -225,7 +225,7 @@ class HTTPResponse: line = self.fp.readline() i = line.find(';') if i >= 0: - line = line[:i] # strip chunk-extensions + line = line[:i] # strip chunk-extensions chunk_left = int(line, 16) if chunk_left == 0: break @@ -237,7 +237,7 @@ class HTTPResponse: return value elif amt == chunk_left: value = value + self._safe_read(amt) - self._safe_read(2) # toss the CRLF at the end of the chunk + self._safe_read(2) # toss the CRLF at the end of the chunk self.chunk_left = None return value else: @@ -245,7 +245,7 @@ class HTTPResponse: amt = amt - chunk_left # we read the whole chunk, get another - self._safe_read(2) # toss the CRLF at the end of the chunk + self._safe_read(2) # toss the CRLF at the end of the chunk chunk_left = None # read and discard trailer up to the CRLF terminator @@ -266,7 +266,7 @@ class HTTPResponse: s = self.fp.read() else: s = self._safe_read(self.length) - self.close() # we read everything + self.close() # we read everything return s if self.length is not None: @@ -355,7 +355,7 @@ class HTTPConnection: def close(self): """Close the connection to the HTTP server.""" if self.sock: - self.sock.close() # close it manually... there may be other refs + self.sock.close() # close it manually... there may be other refs self.sock = None if self.__response: self.__response.close() @@ -380,7 +380,7 @@ class HTTPConnection: try: self.sock.send(str) except socket.error, v: - if v[0] == 32: # Broken pipe + if v[0] == 32: # Broken pipe self.close() raise diff --git a/Lib/ihooks.py b/Lib/ihooks.py index 3f1984c..62760cb 100644 --- a/Lib/ihooks.py +++ b/Lib/ihooks.py @@ -109,7 +109,7 @@ class BasicModuleLoader(_Verbose): """ def find_module(self, name, path = None): - if path is None: + if path is None: path = [None] + self.default_path() for dir in path: stuff = self.find_module_in_dir(name, dir) @@ -390,7 +390,7 @@ class BasicModuleImporter(_Verbose): class ModuleImporter(BasicModuleImporter): """A module importer that supports packages.""" - + def import_module(self, name, globals=None, locals=None, fromlist=None): parent = self.determine_parent(globals) q, tail = self.find_head_package(parent, name) diff --git a/Lib/imaplib.py b/Lib/imaplib.py index 4e34654..954ecf6 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -1,71 +1,70 @@ - """IMAP4 client. Based on RFC 2060. -Public class: IMAP4 -Public variable: Debug -Public functions: Internaldate2tuple - Int2AP - ParseFlags - Time2Internaldate +Public class: IMAP4 +Public variable: Debug +Public functions: Internaldate2tuple + Int2AP + ParseFlags + Time2Internaldate """ # Author: Piers Lauder December 1997. -# +# # Authentication code contributed by Donn Cave June 1998. __version__ = "2.39" import binascii, re, socket, string, time, random, sys -# Globals +# Globals CRLF = '\r\n' Debug = 0 IMAP4_PORT = 143 -AllowedVersions = ('IMAP4REV1', 'IMAP4') # Most recent first +AllowedVersions = ('IMAP4REV1', 'IMAP4') # Most recent first -# Commands +# Commands Commands = { - # name valid states - 'APPEND': ('AUTH', 'SELECTED'), - 'AUTHENTICATE': ('NONAUTH',), - 'CAPABILITY': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'), - 'CHECK': ('SELECTED',), - 'CLOSE': ('SELECTED',), - 'COPY': ('SELECTED',), - 'CREATE': ('AUTH', 'SELECTED'), - 'DELETE': ('AUTH', 'SELECTED'), - 'EXAMINE': ('AUTH', 'SELECTED'), - 'EXPUNGE': ('SELECTED',), - 'FETCH': ('SELECTED',), - 'LIST': ('AUTH', 'SELECTED'), - 'LOGIN': ('NONAUTH',), - 'LOGOUT': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'), - 'LSUB': ('AUTH', 'SELECTED'), - 'NOOP': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'), - 'PARTIAL': ('SELECTED',), - 'RENAME': ('AUTH', 'SELECTED'), - 'SEARCH': ('SELECTED',), - 'SELECT': ('AUTH', 'SELECTED'), - 'STATUS': ('AUTH', 'SELECTED'), - 'STORE': ('SELECTED',), - 'SUBSCRIBE': ('AUTH', 'SELECTED'), - 'UID': ('SELECTED',), - 'UNSUBSCRIBE': ('AUTH', 'SELECTED'), - } - -# Patterns to match server responses + # name valid states + 'APPEND': ('AUTH', 'SELECTED'), + 'AUTHENTICATE': ('NONAUTH',), + 'CAPABILITY': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'), + 'CHECK': ('SELECTED',), + 'CLOSE': ('SELECTED',), + 'COPY': ('SELECTED',), + 'CREATE': ('AUTH', 'SELECTED'), + 'DELETE': ('AUTH', 'SELECTED'), + 'EXAMINE': ('AUTH', 'SELECTED'), + 'EXPUNGE': ('SELECTED',), + 'FETCH': ('SELECTED',), + 'LIST': ('AUTH', 'SELECTED'), + 'LOGIN': ('NONAUTH',), + 'LOGOUT': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'), + 'LSUB': ('AUTH', 'SELECTED'), + 'NOOP': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'), + 'PARTIAL': ('SELECTED',), + 'RENAME': ('AUTH', 'SELECTED'), + 'SEARCH': ('SELECTED',), + 'SELECT': ('AUTH', 'SELECTED'), + 'STATUS': ('AUTH', 'SELECTED'), + 'STORE': ('SELECTED',), + 'SUBSCRIBE': ('AUTH', 'SELECTED'), + 'UID': ('SELECTED',), + 'UNSUBSCRIBE': ('AUTH', 'SELECTED'), + } + +# Patterns to match server responses Continuation = re.compile(r'\+( (?P.*))?') Flags = re.compile(r'.*FLAGS \((?P[^\)]*)\)') InternalDate = re.compile(r'.*INTERNALDATE "' - r'(?P[ 123][0-9])-(?P[A-Z][a-z][a-z])-(?P[0-9][0-9][0-9][0-9])' - r' (?P[0-9][0-9]):(?P[0-9][0-9]):(?P[0-9][0-9])' - r' (?P[-+])(?P[0-9][0-9])(?P[0-9][0-9])' - r'"') + r'(?P[ 123][0-9])-(?P[A-Z][a-z][a-z])-(?P[0-9][0-9][0-9][0-9])' + r' (?P[0-9][0-9]):(?P[0-9][0-9]):(?P[0-9][0-9])' + r' (?P[-+])(?P[0-9][0-9])(?P[0-9][0-9])' + r'"') Literal = re.compile(r'.*{(?P\d+)}$') Response_code = re.compile(r'\[(?P[A-Z-]+)( (?P[^\]]*))?\]') Untagged_response = re.compile(r'\* (?P[A-Z-]+)( (?P.*))?') @@ -75,1045 +74,1045 @@ Untagged_status = re.compile(r'\* (?P\d+) (?P[A-Z-]+)( (?P.*) class IMAP4: - """IMAP4 client class. - - Instantiate with: IMAP4([host[, port]]) - - host - host's name (default: localhost); - port - port number (default: standard IMAP4 port). - - All IMAP4rev1 commands are supported by methods of the same - name (in lower-case). - - All arguments to commands are converted to strings, except for - AUTHENTICATE, and the last argument to APPEND which is passed as - an IMAP4 literal. If necessary (the string contains any - non-printing characters or white-space and isn't enclosed with - either parentheses or double quotes) each string is quoted. - However, the 'password' argument to the LOGIN command is always - quoted. If you want to avoid having an argument string quoted - (eg: the 'flags' argument to STORE) then enclose the string in - parentheses (eg: "(\Deleted)"). + """IMAP4 client class. + + Instantiate with: IMAP4([host[, port]]) + + host - host's name (default: localhost); + port - port number (default: standard IMAP4 port). + + All IMAP4rev1 commands are supported by methods of the same + name (in lower-case). + + All arguments to commands are converted to strings, except for + AUTHENTICATE, and the last argument to APPEND which is passed as + an IMAP4 literal. If necessary (the string contains any + non-printing characters or white-space and isn't enclosed with + either parentheses or double quotes) each string is quoted. + However, the 'password' argument to the LOGIN command is always + quoted. If you want to avoid having an argument string quoted + (eg: the 'flags' argument to STORE) then enclose the string in + parentheses (eg: "(\Deleted)"). - Each command returns a tuple: (type, [data, ...]) where 'type' - is usually 'OK' or 'NO', and 'data' is either the text from the - tagged response, or untagged results from command. + Each command returns a tuple: (type, [data, ...]) where 'type' + is usually 'OK' or 'NO', and 'data' is either the text from the + tagged response, or untagged results from command. - Errors raise the exception class .error(""). - IMAP4 server errors raise .abort(""), - which is a sub-class of 'error'. Mailbox status changes - from READ-WRITE to READ-ONLY raise the exception class - .readonly(""), which is a sub-class of 'abort'. + Errors raise the exception class .error(""). + IMAP4 server errors raise .abort(""), + which is a sub-class of 'error'. Mailbox status changes + from READ-WRITE to READ-ONLY raise the exception class + .readonly(""), which is a sub-class of 'abort'. - "error" exceptions imply a program error. - "abort" exceptions imply the connection should be reset, and - the command re-tried. - "readonly" exceptions imply the command should be re-tried. + "error" exceptions imply a program error. + "abort" exceptions imply the connection should be reset, and + the command re-tried. + "readonly" exceptions imply the command should be re-tried. - Note: to use this module, you must read the RFCs pertaining - to the IMAP4 protocol, as the semantics of the arguments to - each IMAP4 command are left to the invoker, not to mention - the results. - """ + Note: to use this module, you must read the RFCs pertaining + to the IMAP4 protocol, as the semantics of the arguments to + each IMAP4 command are left to the invoker, not to mention + the results. + """ - class error(Exception): pass # Logical errors - debug required - class abort(error): pass # Service errors - close and retry - class readonly(abort): pass # Mailbox status changed to READ-ONLY + class error(Exception): pass # Logical errors - debug required + class abort(error): pass # Service errors - close and retry + class readonly(abort): pass # Mailbox status changed to READ-ONLY - mustquote = re.compile(r"[^\w!#$%&'*+,.:;<=>?^`|~-]") + mustquote = re.compile(r"[^\w!#$%&'*+,.:;<=>?^`|~-]") - def __init__(self, host = '', port = IMAP4_PORT): - self.host = host - self.port = port - self.debug = Debug - self.state = 'LOGOUT' - self.literal = None # A literal argument to a command - self.tagged_commands = {} # Tagged commands awaiting response - self.untagged_responses = {} # {typ: [data, ...], ...} - self.continuation_response = '' # Last continuation response - self.is_readonly = None # READ-ONLY desired state - self.tagnum = 0 + def __init__(self, host = '', port = IMAP4_PORT): + self.host = host + self.port = port + self.debug = Debug + self.state = 'LOGOUT' + self.literal = None # A literal argument to a command + self.tagged_commands = {} # Tagged commands awaiting response + self.untagged_responses = {} # {typ: [data, ...], ...} + self.continuation_response = '' # Last continuation response + self.is_readonly = None # READ-ONLY desired state + self.tagnum = 0 - # Open socket to server. + # Open socket to server. - self.open(host, port) + self.open(host, port) - # Create unique tag for this session, - # and compile tagged response matcher. + # Create unique tag for this session, + # and compile tagged response matcher. - self.tagpre = Int2AP(random.randint(0, 31999)) - self.tagre = re.compile(r'(?P' - + self.tagpre - + r'\d+) (?P[A-Z]+) (?P.*)') + self.tagpre = Int2AP(random.randint(0, 31999)) + self.tagre = re.compile(r'(?P' + + self.tagpre + + r'\d+) (?P[A-Z]+) (?P.*)') - # Get server welcome message, - # request and store CAPABILITY response. + # Get server welcome message, + # request and store CAPABILITY response. - if __debug__: - if self.debug >= 1: - _mesg('new IMAP4 connection, tag=%s' % self.tagpre) + if __debug__: + if self.debug >= 1: + _mesg('new IMAP4 connection, tag=%s' % self.tagpre) - self.welcome = self._get_response() - if self.untagged_responses.has_key('PREAUTH'): - self.state = 'AUTH' - elif self.untagged_responses.has_key('OK'): - self.state = 'NONAUTH' - else: - raise self.error(self.welcome) + self.welcome = self._get_response() + if self.untagged_responses.has_key('PREAUTH'): + self.state = 'AUTH' + elif self.untagged_responses.has_key('OK'): + self.state = 'NONAUTH' + else: + raise self.error(self.welcome) - cap = 'CAPABILITY' - self._simple_command(cap) - if not self.untagged_responses.has_key(cap): - raise self.error('no CAPABILITY response from server') - self.capabilities = tuple(string.split(string.upper(self.untagged_responses[cap][-1]))) + cap = 'CAPABILITY' + self._simple_command(cap) + if not self.untagged_responses.has_key(cap): + raise self.error('no CAPABILITY response from server') + self.capabilities = tuple(string.split(string.upper(self.untagged_responses[cap][-1]))) - if __debug__: - if self.debug >= 3: - _mesg('CAPABILITIES: %s' % `self.capabilities`) + if __debug__: + if self.debug >= 3: + _mesg('CAPABILITIES: %s' % `self.capabilities`) - for version in AllowedVersions: - if not version in self.capabilities: - continue - self.PROTOCOL_VERSION = version - return + for version in AllowedVersions: + if not version in self.capabilities: + continue + self.PROTOCOL_VERSION = version + return - raise self.error('server not IMAP4 compliant') + raise self.error('server not IMAP4 compliant') - def __getattr__(self, attr): - # Allow UPPERCASE variants of IMAP4 command methods. - if Commands.has_key(attr): - return eval("self.%s" % string.lower(attr)) - raise AttributeError("Unknown IMAP4 command: '%s'" % attr) - + def __getattr__(self, attr): + # Allow UPPERCASE variants of IMAP4 command methods. + if Commands.has_key(attr): + return eval("self.%s" % string.lower(attr)) + raise AttributeError("Unknown IMAP4 command: '%s'" % attr) + - # Public methods + # Public methods - def open(self, host, port): - """Setup 'self.sock' and 'self.file'.""" - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((self.host, self.port)) - self.file = self.sock.makefile('r') + def open(self, host, port): + """Setup 'self.sock' and 'self.file'.""" + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((self.host, self.port)) + self.file = self.sock.makefile('r') - def recent(self): - """Return most recent 'RECENT' responses if any exist, - else prompt server for an update using the 'NOOP' command. + def recent(self): + """Return most recent 'RECENT' responses if any exist, + else prompt server for an update using the 'NOOP' command. - (typ, [data]) = .recent() + (typ, [data]) = .recent() - 'data' is None if no new messages, - else list of RECENT responses, most recent last. - """ - name = 'RECENT' - typ, dat = self._untagged_response('OK', [None], name) - if dat[-1]: - return typ, dat - typ, dat = self.noop() # Prod server for response - return self._untagged_response(typ, dat, name) + 'data' is None if no new messages, + else list of RECENT responses, most recent last. + """ + name = 'RECENT' + typ, dat = self._untagged_response('OK', [None], name) + if dat[-1]: + return typ, dat + typ, dat = self.noop() # Prod server for response + return self._untagged_response(typ, dat, name) - def response(self, code): - """Return data for response 'code' if received, or None. + def response(self, code): + """Return data for response 'code' if received, or None. - Old value for response 'code' is cleared. + Old value for response 'code' is cleared. - (code, [data]) = .response(code) - """ - return self._untagged_response(code, [None], string.upper(code)) + (code, [data]) = .response(code) + """ + return self._untagged_response(code, [None], string.upper(code)) - def socket(self): - """Return socket instance used to connect to IMAP4 server. + def socket(self): + """Return socket instance used to connect to IMAP4 server. - socket = .socket() - """ - return self.sock + socket = .socket() + """ + return self.sock - # IMAP4 commands + # IMAP4 commands - def append(self, mailbox, flags, date_time, message): - """Append message to named mailbox. + def append(self, mailbox, flags, date_time, message): + """Append message to named mailbox. - (typ, [data]) = .append(mailbox, flags, date_time, message) + (typ, [data]) = .append(mailbox, flags, date_time, message) - All args except `message' can be None. - """ - name = 'APPEND' - if not mailbox: - mailbox = 'INBOX' - if flags: - if (flags[0],flags[-1]) != ('(',')'): - flags = '(%s)' % flags - else: - flags = None - if date_time: - date_time = Time2Internaldate(date_time) - else: - date_time = None - self.literal = message - return self._simple_command(name, mailbox, flags, date_time) + All args except `message' can be None. + """ + name = 'APPEND' + if not mailbox: + mailbox = 'INBOX' + if flags: + if (flags[0],flags[-1]) != ('(',')'): + flags = '(%s)' % flags + else: + flags = None + if date_time: + date_time = Time2Internaldate(date_time) + else: + date_time = None + self.literal = message + return self._simple_command(name, mailbox, flags, date_time) - def authenticate(self, mechanism, authobject): - """Authenticate command - requires response processing. + def authenticate(self, mechanism, authobject): + """Authenticate command - requires response processing. - 'mechanism' specifies which authentication mechanism is to - be used - it must appear in .capabilities in the - form AUTH=. + 'mechanism' specifies which authentication mechanism is to + be used - it must appear in .capabilities in the + form AUTH=. - 'authobject' must be a callable object: + 'authobject' must be a callable object: - data = authobject(response) + data = authobject(response) - It will be called to process server continuation responses. - It should return data that will be encoded and sent to server. - It should return None if the client abort response '*' should - be sent instead. - """ - mech = string.upper(mechanism) - cap = 'AUTH=%s' % mech - if not cap in self.capabilities: - raise self.error("Server doesn't allow %s authentication." % mech) - self.literal = _Authenticator(authobject).process - typ, dat = self._simple_command('AUTHENTICATE', mech) - if typ != 'OK': - raise self.error(dat[-1]) - self.state = 'AUTH' - return typ, dat + It will be called to process server continuation responses. + It should return data that will be encoded and sent to server. + It should return None if the client abort response '*' should + be sent instead. + """ + mech = string.upper(mechanism) + cap = 'AUTH=%s' % mech + if not cap in self.capabilities: + raise self.error("Server doesn't allow %s authentication." % mech) + self.literal = _Authenticator(authobject).process + typ, dat = self._simple_command('AUTHENTICATE', mech) + if typ != 'OK': + raise self.error(dat[-1]) + self.state = 'AUTH' + return typ, dat - def check(self): - """Checkpoint mailbox on server. + def check(self): + """Checkpoint mailbox on server. - (typ, [data]) = .check() - """ - return self._simple_command('CHECK') + (typ, [data]) = .check() + """ + return self._simple_command('CHECK') - def close(self): - """Close currently selected mailbox. + def close(self): + """Close currently selected mailbox. - Deleted messages are removed from writable mailbox. - This is the recommended command before 'LOGOUT'. + Deleted messages are removed from writable mailbox. + This is the recommended command before 'LOGOUT'. - (typ, [data]) = .close() - """ - try: - typ, dat = self._simple_command('CLOSE') - finally: - self.state = 'AUTH' - return typ, dat + (typ, [data]) = .close() + """ + try: + typ, dat = self._simple_command('CLOSE') + finally: + self.state = 'AUTH' + return typ, dat - def copy(self, message_set, new_mailbox): - """Copy 'message_set' messages onto end of 'new_mailbox'. + def copy(self, message_set, new_mailbox): + """Copy 'message_set' messages onto end of 'new_mailbox'. - (typ, [data]) = .copy(message_set, new_mailbox) - """ - return self._simple_command('COPY', message_set, new_mailbox) + (typ, [data]) = .copy(message_set, new_mailbox) + """ + return self._simple_command('COPY', message_set, new_mailbox) - def create(self, mailbox): - """Create new mailbox. + def create(self, mailbox): + """Create new mailbox. - (typ, [data]) = .create(mailbox) - """ - return self._simple_command('CREATE', mailbox) + (typ, [data]) = .create(mailbox) + """ + return self._simple_command('CREATE', mailbox) - def delete(self, mailbox): - """Delete old mailbox. + def delete(self, mailbox): + """Delete old mailbox. - (typ, [data]) = .delete(mailbox) - """ - return self._simple_command('DELETE', mailbox) + (typ, [data]) = .delete(mailbox) + """ + return self._simple_command('DELETE', mailbox) - def expunge(self): - """Permanently remove deleted items from selected mailbox. + def expunge(self): + """Permanently remove deleted items from selected mailbox. - Generates 'EXPUNGE' response for each deleted message. + Generates 'EXPUNGE' response for each deleted message. - (typ, [data]) = .expunge() + (typ, [data]) = .expunge() - 'data' is list of 'EXPUNGE'd message numbers in order received. - """ - name = 'EXPUNGE' - typ, dat = self._simple_command(name) - return self._untagged_response(typ, dat, name) + 'data' is list of 'EXPUNGE'd message numbers in order received. + """ + name = 'EXPUNGE' + typ, dat = self._simple_command(name) + return self._untagged_response(typ, dat, name) - def fetch(self, message_set, message_parts): - """Fetch (parts of) messages. + def fetch(self, message_set, message_parts): + """Fetch (parts of) messages. - (typ, [data, ...]) = .fetch(message_set, message_parts) + (typ, [data, ...]) = .fetch(message_set, message_parts) - 'message_parts' should be a string of selected parts - enclosed in parentheses, eg: "(UID BODY[TEXT])". + 'message_parts' should be a string of selected parts + enclosed in parentheses, eg: "(UID BODY[TEXT])". - 'data' are tuples of message part envelope and data. - """ - name = 'FETCH' - typ, dat = self._simple_command(name, message_set, message_parts) - return self._untagged_response(typ, dat, name) + 'data' are tuples of message part envelope and data. + """ + name = 'FETCH' + typ, dat = self._simple_command(name, message_set, message_parts) + return self._untagged_response(typ, dat, name) - def list(self, directory='""', pattern='*'): - """List mailbox names in directory matching pattern. + def list(self, directory='""', pattern='*'): + """List mailbox names in directory matching pattern. - (typ, [data]) = .list(directory='""', pattern='*') + (typ, [data]) = .list(directory='""', pattern='*') - 'data' is list of LIST responses. - """ - name = 'LIST' - typ, dat = self._simple_command(name, directory, pattern) - return self._untagged_response(typ, dat, name) + 'data' is list of LIST responses. + """ + name = 'LIST' + typ, dat = self._simple_command(name, directory, pattern) + return self._untagged_response(typ, dat, name) - def login(self, user, password): - """Identify client using plaintext password. + def login(self, user, password): + """Identify client using plaintext password. - (typ, [data]) = .login(user, password) + (typ, [data]) = .login(user, password) - NB: 'password' will be quoted. - """ - #if not 'AUTH=LOGIN' in self.capabilities: - # raise self.error("Server doesn't allow LOGIN authentication." % mech) - typ, dat = self._simple_command('LOGIN', user, self._quote(password)) - if typ != 'OK': - raise self.error(dat[-1]) - self.state = 'AUTH' - return typ, dat + NB: 'password' will be quoted. + """ + #if not 'AUTH=LOGIN' in self.capabilities: + # raise self.error("Server doesn't allow LOGIN authentication." % mech) + typ, dat = self._simple_command('LOGIN', user, self._quote(password)) + if typ != 'OK': + raise self.error(dat[-1]) + self.state = 'AUTH' + return typ, dat - def logout(self): - """Shutdown connection to server. + def logout(self): + """Shutdown connection to server. - (typ, [data]) = .logout() + (typ, [data]) = .logout() - Returns server 'BYE' response. - """ - self.state = 'LOGOUT' - try: typ, dat = self._simple_command('LOGOUT') - except: typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]] - self.file.close() - self.sock.close() - if self.untagged_responses.has_key('BYE'): - return 'BYE', self.untagged_responses['BYE'] - return typ, dat + Returns server 'BYE' response. + """ + self.state = 'LOGOUT' + try: typ, dat = self._simple_command('LOGOUT') + except: typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]] + self.file.close() + self.sock.close() + if self.untagged_responses.has_key('BYE'): + return 'BYE', self.untagged_responses['BYE'] + return typ, dat - def lsub(self, directory='""', pattern='*'): - """List 'subscribed' mailbox names in directory matching pattern. + def lsub(self, directory='""', pattern='*'): + """List 'subscribed' mailbox names in directory matching pattern. - (typ, [data, ...]) = .lsub(directory='""', pattern='*') + (typ, [data, ...]) = .lsub(directory='""', pattern='*') - 'data' are tuples of message part envelope and data. - """ - name = 'LSUB' - typ, dat = self._simple_command(name, directory, pattern) - return self._untagged_response(typ, dat, name) + 'data' are tuples of message part envelope and data. + """ + name = 'LSUB' + typ, dat = self._simple_command(name, directory, pattern) + return self._untagged_response(typ, dat, name) - def noop(self): - """Send NOOP command. + def noop(self): + """Send NOOP command. - (typ, data) = .noop() - """ - if __debug__: - if self.debug >= 3: - _dump_ur(self.untagged_responses) - return self._simple_command('NOOP') + (typ, data) = .noop() + """ + if __debug__: + if self.debug >= 3: + _dump_ur(self.untagged_responses) + return self._simple_command('NOOP') - def partial(self, message_num, message_part, start, length): - """Fetch truncated part of a message. + def partial(self, message_num, message_part, start, length): + """Fetch truncated part of a message. - (typ, [data, ...]) = .partial(message_num, message_part, start, length) + (typ, [data, ...]) = .partial(message_num, message_part, start, length) - 'data' is tuple of message part envelope and data. - """ - name = 'PARTIAL' - typ, dat = self._simple_command(name, message_num, message_part, start, length) - return self._untagged_response(typ, dat, 'FETCH') + 'data' is tuple of message part envelope and data. + """ + name = 'PARTIAL' + typ, dat = self._simple_command(name, message_num, message_part, start, length) + return self._untagged_response(typ, dat, 'FETCH') - def rename(self, oldmailbox, newmailbox): - """Rename old mailbox name to new. + def rename(self, oldmailbox, newmailbox): + """Rename old mailbox name to new. - (typ, data) = .rename(oldmailbox, newmailbox) - """ - return self._simple_command('RENAME', oldmailbox, newmailbox) + (typ, data) = .rename(oldmailbox, newmailbox) + """ + return self._simple_command('RENAME', oldmailbox, newmailbox) - def search(self, charset, *criteria): - """Search mailbox for matching messages. + def search(self, charset, *criteria): + """Search mailbox for matching messages. - (typ, [data]) = .search(charset, criterium, ...) + (typ, [data]) = .search(charset, criterium, ...) - 'data' is space separated list of matching message numbers. - """ - name = 'SEARCH' - if charset: - charset = 'CHARSET ' + charset - typ, dat = apply(self._simple_command, (name, charset) + criteria) - return self._untagged_response(typ, dat, name) + 'data' is space separated list of matching message numbers. + """ + name = 'SEARCH' + if charset: + charset = 'CHARSET ' + charset + typ, dat = apply(self._simple_command, (name, charset) + criteria) + return self._untagged_response(typ, dat, name) - def select(self, mailbox='INBOX', readonly=None): - """Select a mailbox. + def select(self, mailbox='INBOX', readonly=None): + """Select a mailbox. - Flush all untagged responses. + Flush all untagged responses. - (typ, [data]) = .select(mailbox='INBOX', readonly=None) + (typ, [data]) = .select(mailbox='INBOX', readonly=None) - 'data' is count of messages in mailbox ('EXISTS' response). - """ - # Mandated responses are ('FLAGS', 'EXISTS', 'RECENT', 'UIDVALIDITY') - self.untagged_responses = {} # Flush old responses. - self.is_readonly = readonly - if readonly: - name = 'EXAMINE' - else: - name = 'SELECT' - typ, dat = self._simple_command(name, mailbox) - if typ != 'OK': - self.state = 'AUTH' # Might have been 'SELECTED' - return typ, dat - self.state = 'SELECTED' - if self.untagged_responses.has_key('READ-ONLY') \ - and not readonly: - if __debug__: - if self.debug >= 1: - _dump_ur(self.untagged_responses) - raise self.readonly('%s is not writable' % mailbox) - return typ, self.untagged_responses.get('EXISTS', [None]) + 'data' is count of messages in mailbox ('EXISTS' response). + """ + # Mandated responses are ('FLAGS', 'EXISTS', 'RECENT', 'UIDVALIDITY') + self.untagged_responses = {} # Flush old responses. + self.is_readonly = readonly + if readonly: + name = 'EXAMINE' + else: + name = 'SELECT' + typ, dat = self._simple_command(name, mailbox) + if typ != 'OK': + self.state = 'AUTH' # Might have been 'SELECTED' + return typ, dat + self.state = 'SELECTED' + if self.untagged_responses.has_key('READ-ONLY') \ + and not readonly: + if __debug__: + if self.debug >= 1: + _dump_ur(self.untagged_responses) + raise self.readonly('%s is not writable' % mailbox) + return typ, self.untagged_responses.get('EXISTS', [None]) - def status(self, mailbox, names): - """Request named status conditions for mailbox. + def status(self, mailbox, names): + """Request named status conditions for mailbox. - (typ, [data]) = .status(mailbox, names) - """ - name = 'STATUS' - if self.PROTOCOL_VERSION == 'IMAP4': - raise self.error('%s unimplemented in IMAP4 (obtain IMAP4rev1 server, or re-code)' % name) - typ, dat = self._simple_command(name, mailbox, names) - return self._untagged_response(typ, dat, name) + (typ, [data]) = .status(mailbox, names) + """ + name = 'STATUS' + if self.PROTOCOL_VERSION == 'IMAP4': + raise self.error('%s unimplemented in IMAP4 (obtain IMAP4rev1 server, or re-code)' % name) + typ, dat = self._simple_command(name, mailbox, names) + return self._untagged_response(typ, dat, name) - def store(self, message_set, command, flags): - """Alters flag dispositions for messages in mailbox. + def store(self, message_set, command, flags): + """Alters flag dispositions for messages in mailbox. - (typ, [data]) = .store(message_set, command, flags) - """ - if (flags[0],flags[-1]) != ('(',')'): - flags = '(%s)' % flags # Avoid quoting the flags - typ, dat = self._simple_command('STORE', message_set, command, flags) - return self._untagged_response(typ, dat, 'FETCH') + (typ, [data]) = .store(message_set, command, flags) + """ + if (flags[0],flags[-1]) != ('(',')'): + flags = '(%s)' % flags # Avoid quoting the flags + typ, dat = self._simple_command('STORE', message_set, command, flags) + return self._untagged_response(typ, dat, 'FETCH') - def subscribe(self, mailbox): - """Subscribe to new mailbox. + def subscribe(self, mailbox): + """Subscribe to new mailbox. - (typ, [data]) = .subscribe(mailbox) - """ - return self._simple_command('SUBSCRIBE', mailbox) + (typ, [data]) = .subscribe(mailbox) + """ + return self._simple_command('SUBSCRIBE', mailbox) - def uid(self, command, *args): - """Execute "command arg ..." with messages identified by UID, - rather than message number. + def uid(self, command, *args): + """Execute "command arg ..." with messages identified by UID, + rather than message number. - (typ, [data]) = .uid(command, arg1, arg2, ...) + (typ, [data]) = .uid(command, arg1, arg2, ...) - Returns response appropriate to 'command'. - """ - command = string.upper(command) - if not Commands.has_key(command): - raise self.error("Unknown IMAP4 UID command: %s" % command) - if self.state not in Commands[command]: - raise self.error('command %s illegal in state %s' - % (command, self.state)) - name = 'UID' - typ, dat = apply(self._simple_command, (name, command) + args) - if command == 'SEARCH': - name = 'SEARCH' - else: - name = 'FETCH' - return self._untagged_response(typ, dat, name) + Returns response appropriate to 'command'. + """ + command = string.upper(command) + if not Commands.has_key(command): + raise self.error("Unknown IMAP4 UID command: %s" % command) + if self.state not in Commands[command]: + raise self.error('command %s illegal in state %s' + % (command, self.state)) + name = 'UID' + typ, dat = apply(self._simple_command, (name, command) + args) + if command == 'SEARCH': + name = 'SEARCH' + else: + name = 'FETCH' + return self._untagged_response(typ, dat, name) - def unsubscribe(self, mailbox): - """Unsubscribe from old mailbox. + def unsubscribe(self, mailbox): + """Unsubscribe from old mailbox. - (typ, [data]) = .unsubscribe(mailbox) - """ - return self._simple_command('UNSUBSCRIBE', mailbox) - - - def xatom(self, name, *args): - """Allow simple extension commands - notified by server in CAPABILITY response. + (typ, [data]) = .unsubscribe(mailbox) + """ + return self._simple_command('UNSUBSCRIBE', mailbox) + + + def xatom(self, name, *args): + """Allow simple extension commands + notified by server in CAPABILITY response. - (typ, [data]) = .xatom(name, arg, ...) - """ - if name[0] != 'X' or not name in self.capabilities: - raise self.error('unknown extension command: %s' % name) - return apply(self._simple_command, (name,) + args) + (typ, [data]) = .xatom(name, arg, ...) + """ + if name[0] != 'X' or not name in self.capabilities: + raise self.error('unknown extension command: %s' % name) + return apply(self._simple_command, (name,) + args) - - # Private methods - - - def _append_untagged(self, typ, dat): - - if dat is None: dat = '' - ur = self.untagged_responses - if __debug__: - if self.debug >= 5: - _mesg('untagged_responses[%s] %s += ["%s"]' % - (typ, len(ur.get(typ,'')), dat)) - if ur.has_key(typ): - ur[typ].append(dat) - else: - ur[typ] = [dat] + + # Private methods + + + def _append_untagged(self, typ, dat): + + if dat is None: dat = '' + ur = self.untagged_responses + if __debug__: + if self.debug >= 5: + _mesg('untagged_responses[%s] %s += ["%s"]' % + (typ, len(ur.get(typ,'')), dat)) + if ur.has_key(typ): + ur[typ].append(dat) + else: + ur[typ] = [dat] - def _check_bye(self): - bye = self.untagged_responses.get('BYE') - if bye: - raise self.abort(bye[-1]) + def _check_bye(self): + bye = self.untagged_responses.get('BYE') + if bye: + raise self.abort(bye[-1]) - def _command(self, name, *args): + def _command(self, name, *args): - if self.state not in Commands[name]: - self.literal = None - raise self.error( - 'command %s illegal in state %s' % (name, self.state)) + if self.state not in Commands[name]: + self.literal = None + raise self.error( + 'command %s illegal in state %s' % (name, self.state)) - for typ in ('OK', 'NO', 'BAD'): - if self.untagged_responses.has_key(typ): - del self.untagged_responses[typ] + for typ in ('OK', 'NO', 'BAD'): + if self.untagged_responses.has_key(typ): + del self.untagged_responses[typ] - if self.untagged_responses.has_key('READ-ONLY') \ - and not self.is_readonly: - raise self.readonly('mailbox status changed to READ-ONLY') + if self.untagged_responses.has_key('READ-ONLY') \ + and not self.is_readonly: + raise self.readonly('mailbox status changed to READ-ONLY') - tag = self._new_tag() - data = '%s %s' % (tag, name) - for arg in args: - if arg is None: continue - data = '%s %s' % (data, self._checkquote(arg)) + tag = self._new_tag() + data = '%s %s' % (tag, name) + for arg in args: + if arg is None: continue + data = '%s %s' % (data, self._checkquote(arg)) - literal = self.literal - if literal is not None: - self.literal = None - if type(literal) is type(self._command): - literator = literal - else: - literator = None - data = '%s {%s}' % (data, len(literal)) + literal = self.literal + if literal is not None: + self.literal = None + if type(literal) is type(self._command): + literator = literal + else: + literator = None + data = '%s {%s}' % (data, len(literal)) - if __debug__: - if self.debug >= 4: - _mesg('> %s' % data) - else: - _log('> %s' % data) + if __debug__: + if self.debug >= 4: + _mesg('> %s' % data) + else: + _log('> %s' % data) - try: - self.sock.send('%s%s' % (data, CRLF)) - except socket.error, val: - raise self.abort('socket error: %s' % val) + try: + self.sock.send('%s%s' % (data, CRLF)) + except socket.error, val: + raise self.abort('socket error: %s' % val) - if literal is None: - return tag + if literal is None: + return tag - while 1: - # Wait for continuation response + while 1: + # Wait for continuation response - while self._get_response(): - if self.tagged_commands[tag]: # BAD/NO? - return tag + while self._get_response(): + if self.tagged_commands[tag]: # BAD/NO? + return tag - # Send literal + # Send literal - if literator: - literal = literator(self.continuation_response) + if literator: + literal = literator(self.continuation_response) - if __debug__: - if self.debug >= 4: - _mesg('write literal size %s' % len(literal)) + if __debug__: + if self.debug >= 4: + _mesg('write literal size %s' % len(literal)) - try: - self.sock.send(literal) - self.sock.send(CRLF) - except socket.error, val: - raise self.abort('socket error: %s' % val) + try: + self.sock.send(literal) + self.sock.send(CRLF) + except socket.error, val: + raise self.abort('socket error: %s' % val) - if not literator: - break + if not literator: + break - return tag + return tag - def _command_complete(self, name, tag): - self._check_bye() - try: - typ, data = self._get_tagged_response(tag) - except self.abort, val: - raise self.abort('command: %s => %s' % (name, val)) - except self.error, val: - raise self.error('command: %s => %s' % (name, val)) - self._check_bye() - if typ == 'BAD': - raise self.error('%s command error: %s %s' % (name, typ, data)) - return typ, data + def _command_complete(self, name, tag): + self._check_bye() + try: + typ, data = self._get_tagged_response(tag) + except self.abort, val: + raise self.abort('command: %s => %s' % (name, val)) + except self.error, val: + raise self.error('command: %s => %s' % (name, val)) + self._check_bye() + if typ == 'BAD': + raise self.error('%s command error: %s %s' % (name, typ, data)) + return typ, data - def _get_response(self): + def _get_response(self): - # Read response and store. - # - # Returns None for continuation responses, - # otherwise first response line received. + # Read response and store. + # + # Returns None for continuation responses, + # otherwise first response line received. - resp = self._get_line() + resp = self._get_line() - # Command completion response? + # Command completion response? - if self._match(self.tagre, resp): - tag = self.mo.group('tag') - if not self.tagged_commands.has_key(tag): - raise self.abort('unexpected tagged response: %s' % resp) + if self._match(self.tagre, resp): + tag = self.mo.group('tag') + if not self.tagged_commands.has_key(tag): + raise self.abort('unexpected tagged response: %s' % resp) - typ = self.mo.group('type') - dat = self.mo.group('data') - self.tagged_commands[tag] = (typ, [dat]) - else: - dat2 = None + typ = self.mo.group('type') + dat = self.mo.group('data') + self.tagged_commands[tag] = (typ, [dat]) + else: + dat2 = None - # '*' (untagged) responses? + # '*' (untagged) responses? - if not self._match(Untagged_response, resp): - if self._match(Untagged_status, resp): - dat2 = self.mo.group('data2') + if not self._match(Untagged_response, resp): + if self._match(Untagged_status, resp): + dat2 = self.mo.group('data2') - if self.mo is None: - # Only other possibility is '+' (continuation) response... + if self.mo is None: + # Only other possibility is '+' (continuation) response... - if self._match(Continuation, resp): - self.continuation_response = self.mo.group('data') - return None # NB: indicates continuation + if self._match(Continuation, resp): + self.continuation_response = self.mo.group('data') + return None # NB: indicates continuation - raise self.abort("unexpected response: '%s'" % resp) + raise self.abort("unexpected response: '%s'" % resp) - typ = self.mo.group('type') - dat = self.mo.group('data') - if dat is None: dat = '' # Null untagged response - if dat2: dat = dat + ' ' + dat2 + typ = self.mo.group('type') + dat = self.mo.group('data') + if dat is None: dat = '' # Null untagged response + if dat2: dat = dat + ' ' + dat2 - # Is there a literal to come? + # Is there a literal to come? - while self._match(Literal, dat): + while self._match(Literal, dat): - # Read literal direct from connection. + # Read literal direct from connection. - size = string.atoi(self.mo.group('size')) - if __debug__: - if self.debug >= 4: - _mesg('read literal size %s' % size) - data = self.file.read(size) + size = string.atoi(self.mo.group('size')) + if __debug__: + if self.debug >= 4: + _mesg('read literal size %s' % size) + data = self.file.read(size) - # Store response with literal as tuple + # Store response with literal as tuple - self._append_untagged(typ, (dat, data)) + self._append_untagged(typ, (dat, data)) - # Read trailer - possibly containing another literal + # Read trailer - possibly containing another literal - dat = self._get_line() + dat = self._get_line() - self._append_untagged(typ, dat) + self._append_untagged(typ, dat) - # Bracketed response information? + # Bracketed response information? - if typ in ('OK', 'NO', 'BAD') and self._match(Response_code, dat): - self._append_untagged(self.mo.group('type'), self.mo.group('data')) + if typ in ('OK', 'NO', 'BAD') and self._match(Response_code, dat): + self._append_untagged(self.mo.group('type'), self.mo.group('data')) - if __debug__: - if self.debug >= 1 and typ in ('NO', 'BAD', 'BYE'): - _mesg('%s response: %s' % (typ, dat)) + if __debug__: + if self.debug >= 1 and typ in ('NO', 'BAD', 'BYE'): + _mesg('%s response: %s' % (typ, dat)) - return resp + return resp - def _get_tagged_response(self, tag): + def _get_tagged_response(self, tag): - while 1: - result = self.tagged_commands[tag] - if result is not None: - del self.tagged_commands[tag] - return result + while 1: + result = self.tagged_commands[tag] + if result is not None: + del self.tagged_commands[tag] + return result - # Some have reported "unexpected response" exceptions. - # Note that ignoring them here causes loops. - # Instead, send me details of the unexpected response and - # I'll update the code in `_get_response()'. + # Some have reported "unexpected response" exceptions. + # Note that ignoring them here causes loops. + # Instead, send me details of the unexpected response and + # I'll update the code in `_get_response()'. - try: - self._get_response() - except self.abort, val: - if __debug__: - if self.debug >= 1: - print_log() - raise + try: + self._get_response() + except self.abort, val: + if __debug__: + if self.debug >= 1: + print_log() + raise - def _get_line(self): + def _get_line(self): - line = self.file.readline() - if not line: - raise self.abort('socket error: EOF') + line = self.file.readline() + if not line: + raise self.abort('socket error: EOF') - # Protocol mandates all lines terminated by CRLF + # Protocol mandates all lines terminated by CRLF - line = line[:-2] - if __debug__: - if self.debug >= 4: - _mesg('< %s' % line) - else: - _log('< %s' % line) - return line + line = line[:-2] + if __debug__: + if self.debug >= 4: + _mesg('< %s' % line) + else: + _log('< %s' % line) + return line - def _match(self, cre, s): + def _match(self, cre, s): - # Run compiled regular expression match method on 's'. - # Save result, return success. + # Run compiled regular expression match method on 's'. + # Save result, return success. - self.mo = cre.match(s) - if __debug__: - if self.mo is not None and self.debug >= 5: - _mesg("\tmatched r'%s' => %s" % (cre.pattern, `self.mo.groups()`)) - return self.mo is not None + self.mo = cre.match(s) + if __debug__: + if self.mo is not None and self.debug >= 5: + _mesg("\tmatched r'%s' => %s" % (cre.pattern, `self.mo.groups()`)) + return self.mo is not None - def _new_tag(self): + def _new_tag(self): - tag = '%s%s' % (self.tagpre, self.tagnum) - self.tagnum = self.tagnum + 1 - self.tagged_commands[tag] = None - return tag + tag = '%s%s' % (self.tagpre, self.tagnum) + self.tagnum = self.tagnum + 1 + self.tagged_commands[tag] = None + return tag - def _checkquote(self, arg): + def _checkquote(self, arg): - # Must quote command args if non-alphanumeric chars present, - # and not already quoted. + # Must quote command args if non-alphanumeric chars present, + # and not already quoted. - if type(arg) is not type(''): - return arg - if (arg[0],arg[-1]) in (('(',')'),('"','"')): - return arg - if self.mustquote.search(arg) is None: - return arg - return self._quote(arg) + if type(arg) is not type(''): + return arg + if (arg[0],arg[-1]) in (('(',')'),('"','"')): + return arg + if self.mustquote.search(arg) is None: + return arg + return self._quote(arg) - def _quote(self, arg): + def _quote(self, arg): - arg = string.replace(arg, '\\', '\\\\') - arg = string.replace(arg, '"', '\\"') + arg = string.replace(arg, '\\', '\\\\') + arg = string.replace(arg, '"', '\\"') - return '"%s"' % arg + return '"%s"' % arg - def _simple_command(self, name, *args): + def _simple_command(self, name, *args): - return self._command_complete(name, apply(self._command, (name,) + args)) + return self._command_complete(name, apply(self._command, (name,) + args)) - def _untagged_response(self, typ, dat, name): + def _untagged_response(self, typ, dat, name): - if typ == 'NO': - return typ, dat - if not self.untagged_responses.has_key(name): - return typ, [None] - data = self.untagged_responses[name] - if __debug__: - if self.debug >= 5: - _mesg('untagged_responses[%s] => %s' % (name, data)) - del self.untagged_responses[name] - return typ, data + if typ == 'NO': + return typ, dat + if not self.untagged_responses.has_key(name): + return typ, [None] + data = self.untagged_responses[name] + if __debug__: + if self.debug >= 5: + _mesg('untagged_responses[%s] => %s' % (name, data)) + del self.untagged_responses[name] + return typ, data class _Authenticator: - """Private class to provide en/decoding - for base64-based authentication conversation. - """ - - def __init__(self, mechinst): - self.mech = mechinst # Callable object to provide/process data - - def process(self, data): - ret = self.mech(self.decode(data)) - if ret is None: - return '*' # Abort conversation - return self.encode(ret) - - def encode(self, inp): - # - # Invoke binascii.b2a_base64 iteratively with - # short even length buffers, strip the trailing - # line feed from the result and append. "Even" - # means a number that factors to both 6 and 8, - # so when it gets to the end of the 8-bit input - # there's no partial 6-bit output. - # - oup = '' - while inp: - if len(inp) > 48: - t = inp[:48] - inp = inp[48:] - else: - t = inp - inp = '' - e = binascii.b2a_base64(t) - if e: - oup = oup + e[:-1] - return oup - - def decode(self, inp): - if not inp: - return '' - return binascii.a2b_base64(inp) - + """Private class to provide en/decoding + for base64-based authentication conversation. + """ + + def __init__(self, mechinst): + self.mech = mechinst # Callable object to provide/process data + + def process(self, data): + ret = self.mech(self.decode(data)) + if ret is None: + return '*' # Abort conversation + return self.encode(ret) + + def encode(self, inp): + # + # Invoke binascii.b2a_base64 iteratively with + # short even length buffers, strip the trailing + # line feed from the result and append. "Even" + # means a number that factors to both 6 and 8, + # so when it gets to the end of the 8-bit input + # there's no partial 6-bit output. + # + oup = '' + while inp: + if len(inp) > 48: + t = inp[:48] + inp = inp[48:] + else: + t = inp + inp = '' + e = binascii.b2a_base64(t) + if e: + oup = oup + e[:-1] + return oup + + def decode(self, inp): + if not inp: + return '' + return binascii.a2b_base64(inp) + Mon2num = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, - 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12} + 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12} def Internaldate2tuple(resp): - """Convert IMAP4 INTERNALDATE to UT. + """Convert IMAP4 INTERNALDATE to UT. - Returns Python time module tuple. - """ + Returns Python time module tuple. + """ - mo = InternalDate.match(resp) - if not mo: - return None + mo = InternalDate.match(resp) + if not mo: + return None - mon = Mon2num[mo.group('mon')] - zonen = mo.group('zonen') + mon = Mon2num[mo.group('mon')] + zonen = mo.group('zonen') - for name in ('day', 'year', 'hour', 'min', 'sec', 'zoneh', 'zonem'): - exec "%s = string.atoi(mo.group('%s'))" % (name, name) + for name in ('day', 'year', 'hour', 'min', 'sec', 'zoneh', 'zonem'): + exec "%s = string.atoi(mo.group('%s'))" % (name, name) - # INTERNALDATE timezone must be subtracted to get UT + # INTERNALDATE timezone must be subtracted to get UT - zone = (zoneh*60 + zonem)*60 - if zonen == '-': - zone = -zone + zone = (zoneh*60 + zonem)*60 + if zonen == '-': + zone = -zone - tt = (year, mon, day, hour, min, sec, -1, -1, -1) + tt = (year, mon, day, hour, min, sec, -1, -1, -1) - utc = time.mktime(tt) + utc = time.mktime(tt) - # Following is necessary because the time module has no 'mkgmtime'. - # 'mktime' assumes arg in local timezone, so adds timezone/altzone. + # Following is necessary because the time module has no 'mkgmtime'. + # 'mktime' assumes arg in local timezone, so adds timezone/altzone. - lt = time.localtime(utc) - if time.daylight and lt[-1]: - zone = zone + time.altzone - else: - zone = zone + time.timezone + lt = time.localtime(utc) + if time.daylight and lt[-1]: + zone = zone + time.altzone + else: + zone = zone + time.timezone - return time.localtime(utc - zone) + return time.localtime(utc - zone) def Int2AP(num): - """Convert integer to A-P string representation.""" + """Convert integer to A-P string representation.""" - val = ''; AP = 'ABCDEFGHIJKLMNOP' - num = int(abs(num)) - while num: - num, mod = divmod(num, 16) - val = AP[mod] + val - return val + val = ''; AP = 'ABCDEFGHIJKLMNOP' + num = int(abs(num)) + while num: + num, mod = divmod(num, 16) + val = AP[mod] + val + return val def ParseFlags(resp): - """Convert IMAP4 flags response to python tuple.""" + """Convert IMAP4 flags response to python tuple.""" - mo = Flags.match(resp) - if not mo: - return () + mo = Flags.match(resp) + if not mo: + return () - return tuple(string.split(mo.group('flags'))) + return tuple(string.split(mo.group('flags'))) def Time2Internaldate(date_time): - """Convert 'date_time' to IMAP4 INTERNALDATE representation. + """Convert 'date_time' to IMAP4 INTERNALDATE representation. - Return string in form: '"DD-Mmm-YYYY HH:MM:SS +HHMM"' - """ + Return string in form: '"DD-Mmm-YYYY HH:MM:SS +HHMM"' + """ - dttype = type(date_time) - if dttype is type(1) or dttype is type(1.1): - tt = time.localtime(date_time) - elif dttype is type(()): - tt = date_time - elif dttype is type(""): - return date_time # Assume in correct format - else: raise ValueError + dttype = type(date_time) + if dttype is type(1) or dttype is type(1.1): + tt = time.localtime(date_time) + elif dttype is type(()): + tt = date_time + elif dttype is type(""): + return date_time # Assume in correct format + else: raise ValueError - dt = time.strftime("%d-%b-%Y %H:%M:%S", tt) - if dt[0] == '0': - dt = ' ' + dt[1:] - if time.daylight and tt[-1]: - zone = -time.altzone - else: - zone = -time.timezone - return '"' + dt + " %+02d%02d" % divmod(zone/60, 60) + '"' + dt = time.strftime("%d-%b-%Y %H:%M:%S", tt) + if dt[0] == '0': + dt = ' ' + dt[1:] + if time.daylight and tt[-1]: + zone = -time.altzone + else: + zone = -time.timezone + return '"' + dt + " %+02d%02d" % divmod(zone/60, 60) + '"' if __debug__: - def _mesg(s, secs=None): - if secs is None: - secs = time.time() - tm = time.strftime('%M:%S', time.localtime(secs)) - sys.stderr.write(' %s.%02d %s\n' % (tm, (secs*100)%100, s)) - sys.stderr.flush() + def _mesg(s, secs=None): + if secs is None: + secs = time.time() + tm = time.strftime('%M:%S', time.localtime(secs)) + sys.stderr.write(' %s.%02d %s\n' % (tm, (secs*100)%100, s)) + sys.stderr.flush() - def _dump_ur(dict): - # Dump untagged responses (in `dict'). - l = dict.items() - if not l: return - t = '\n\t\t' - j = string.join - l = map(lambda x,j=j:'%s: "%s"' % (x[0], x[1][0] and j(x[1], '" "') or ''), l) - _mesg('untagged responses dump:%s%s' % (t, j(l, t))) + def _dump_ur(dict): + # Dump untagged responses (in `dict'). + l = dict.items() + if not l: return + t = '\n\t\t' + j = string.join + l = map(lambda x,j=j:'%s: "%s"' % (x[0], x[1][0] and j(x[1], '" "') or ''), l) + _mesg('untagged responses dump:%s%s' % (t, j(l, t))) - _cmd_log = [] # Last `_cmd_log_len' interactions - _cmd_log_len = 10 + _cmd_log = [] # Last `_cmd_log_len' interactions + _cmd_log_len = 10 - def _log(line): - # Keep log of last `_cmd_log_len' interactions for debugging. - if len(_cmd_log) == _cmd_log_len: - del _cmd_log[0] - _cmd_log.append((time.time(), line)) + def _log(line): + # Keep log of last `_cmd_log_len' interactions for debugging. + if len(_cmd_log) == _cmd_log_len: + del _cmd_log[0] + _cmd_log.append((time.time(), line)) - def print_log(): - _mesg('last %d IMAP4 interactions:' % len(_cmd_log)) - for secs,line in _cmd_log: - _mesg(line, secs) + def print_log(): + _mesg('last %d IMAP4 interactions:' % len(_cmd_log)) + for secs,line in _cmd_log: + _mesg(line, secs) if __name__ == '__main__': - import getopt, getpass, sys - - try: - optlist, args = getopt.getopt(sys.argv[1:], 'd:') - except getopt.error, val: - pass - - for opt,val in optlist: - if opt == '-d': - Debug = int(val) - - if not args: args = ('',) - - host = args[0] - - USER = getpass.getuser() - PASSWD = getpass.getpass("IMAP password for %s on %s" % (USER, host or "localhost")) - - test_mesg = 'From: %s@localhost\nSubject: IMAP4 test\n\ndata...\n' % USER - test_seq1 = ( - ('login', (USER, PASSWD)), - ('create', ('/tmp/xxx 1',)), - ('rename', ('/tmp/xxx 1', '/tmp/yyy')), - ('CREATE', ('/tmp/yyz 2',)), - ('append', ('/tmp/yyz 2', None, None, test_mesg)), - ('list', ('/tmp', 'yy*')), - ('select', ('/tmp/yyz 2',)), - ('search', (None, 'SUBJECT', 'test')), - ('partial', ('1', 'RFC822', 1, 1024)), - ('store', ('1', 'FLAGS', '(\Deleted)')), - ('expunge', ()), - ('recent', ()), - ('close', ()), - ) - - test_seq2 = ( - ('select', ()), - ('response',('UIDVALIDITY',)), - ('uid', ('SEARCH', 'ALL')), - ('response', ('EXISTS',)), - ('append', (None, None, None, test_mesg)), - ('recent', ()), - ('logout', ()), - ) - - def run(cmd, args): - _mesg('%s %s' % (cmd, args)) - typ, dat = apply(eval('M.%s' % cmd), args) - _mesg('%s => %s %s' % (cmd, typ, dat)) - return dat - - try: - M = IMAP4(host) - _mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION) - - for cmd,args in test_seq1: - run(cmd, args) - - for ml in run('list', ('/tmp/', 'yy%')): - mo = re.match(r'.*"([^"]+)"$', ml) - if mo: path = mo.group(1) - else: path = string.split(ml)[-1] - run('delete', (path,)) - - for cmd,args in test_seq2: - dat = run(cmd, args) - - if (cmd,args) != ('uid', ('SEARCH', 'ALL')): - continue - - uid = string.split(dat[-1]) - if not uid: continue - run('uid', ('FETCH', '%s' % uid[-1], - '(FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER RFC822.TEXT)')) - - print '\nAll tests OK.' - - except: - print '\nTests failed.' - - if not Debug: - print ''' + import getopt, getpass, sys + + try: + optlist, args = getopt.getopt(sys.argv[1:], 'd:') + except getopt.error, val: + pass + + for opt,val in optlist: + if opt == '-d': + Debug = int(val) + + if not args: args = ('',) + + host = args[0] + + USER = getpass.getuser() + PASSWD = getpass.getpass("IMAP password for %s on %s" % (USER, host or "localhost")) + + test_mesg = 'From: %s@localhost\nSubject: IMAP4 test\n\ndata...\n' % USER + test_seq1 = ( + ('login', (USER, PASSWD)), + ('create', ('/tmp/xxx 1',)), + ('rename', ('/tmp/xxx 1', '/tmp/yyy')), + ('CREATE', ('/tmp/yyz 2',)), + ('append', ('/tmp/yyz 2', None, None, test_mesg)), + ('list', ('/tmp', 'yy*')), + ('select', ('/tmp/yyz 2',)), + ('search', (None, 'SUBJECT', 'test')), + ('partial', ('1', 'RFC822', 1, 1024)), + ('store', ('1', 'FLAGS', '(\Deleted)')), + ('expunge', ()), + ('recent', ()), + ('close', ()), + ) + + test_seq2 = ( + ('select', ()), + ('response',('UIDVALIDITY',)), + ('uid', ('SEARCH', 'ALL')), + ('response', ('EXISTS',)), + ('append', (None, None, None, test_mesg)), + ('recent', ()), + ('logout', ()), + ) + + def run(cmd, args): + _mesg('%s %s' % (cmd, args)) + typ, dat = apply(eval('M.%s' % cmd), args) + _mesg('%s => %s %s' % (cmd, typ, dat)) + return dat + + try: + M = IMAP4(host) + _mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION) + + for cmd,args in test_seq1: + run(cmd, args) + + for ml in run('list', ('/tmp/', 'yy%')): + mo = re.match(r'.*"([^"]+)"$', ml) + if mo: path = mo.group(1) + else: path = string.split(ml)[-1] + run('delete', (path,)) + + for cmd,args in test_seq2: + dat = run(cmd, args) + + if (cmd,args) != ('uid', ('SEARCH', 'ALL')): + continue + + uid = string.split(dat[-1]) + if not uid: continue + run('uid', ('FETCH', '%s' % uid[-1], + '(FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER RFC822.TEXT)')) + + print '\nAll tests OK.' + + except: + print '\nTests failed.' + + if not Debug: + print ''' If you would like to see debugging output, try: %s -d5 ''' % sys.argv[0] - raise + raise diff --git a/Lib/imghdr.py b/Lib/imghdr.py index 5a538d5..97687b0 100644 --- a/Lib/imghdr.py +++ b/Lib/imghdr.py @@ -14,7 +14,7 @@ def what(file, h=None): location = file.tell() h = file.read(32) file.seek(location) - f = None + f = None else: f = None try: @@ -103,7 +103,7 @@ tests.append(test_jpeg) def test_bmp(h, f): if h[:2] == 'BM': return 'bmp' - + tests.append(test_bmp) def test_png(h, f): diff --git a/Lib/imputil.py b/Lib/imputil.py index 0dd7b51..ca359e95 100644 --- a/Lib/imputil.py +++ b/Lib/imputil.py @@ -5,7 +5,7 @@ ### docco needed here and in Docs/ ... # note: avoid importing non-builtin modules -import imp ### not available in JPython? +import imp ### not available in JPython? import sys import strop import __builtin__ @@ -15,7 +15,7 @@ import struct import marshal _StringType = type('') -_ModuleType = type(sys) ### doesn't work in JPython... +_ModuleType = type(sys) ### doesn't work in JPython... class ImportManager: "Manage the import process." @@ -663,7 +663,7 @@ def _test_revamp(): # # # Guido's comments on sys.path caching: -# +# # We could cache this in a dictionary: the ImportManager can have a # cache dict mapping pathnames to importer objects, and a separate # method for coming up with an importer given a pathname that's not yet @@ -679,16 +679,16 @@ def _test_revamp(): # My/Guido's comments on factoring ImportManager and Importer: # # > However, we still have a tension occurring here: -# > +# > # > 1) implementing policy in ImportManager assists in single-point policy # > changes for app/rexec situations # > 2) implementing policy in Importer assists in package-private policy # > changes for normal, operating conditions -# > +# > # > I'll see if I can sort out a way to do this. Maybe the Importer class will # > implement the methods (which can be overridden to change policy) by # > delegating to ImportManager. -# +# # Maybe also think about what kind of policies an Importer would be # likely to want to change. I have a feeling that a lot of the code # there is actually not so much policy but a *necessity* to get things diff --git a/Lib/macurl2path.py b/Lib/macurl2path.py index 9daeae4..aab085e 100644 --- a/Lib/macurl2path.py +++ b/Lib/macurl2path.py @@ -16,7 +16,7 @@ def url2pathname(pathname): raise RuntimeError, 'Cannot convert non-local URL to pathname' # Turn starting /// into /, an empty hostname means current host if pathname[:3] == '///': - pathname = pathname[2:] + pathname = pathname[2:] elif pathname[:2] == '//': raise RuntimeError, 'Cannot convert non-local URL to pathname' components = string.split(pathname, '/') @@ -68,11 +68,11 @@ def pathname2url(pathname): return '/' + string.join(components, '/') else: return string.join(components, '/') - + def _pncomp2url(component): - component = urllib.quote(component[:31], safe='') # We want to quote slashes - return component - + component = urllib.quote(component[:31], safe='') # We want to quote slashes + return component + def test(): for url in ["index.html", "bar/index.html", diff --git a/Lib/mailcap.py b/Lib/mailcap.py index 9756594..2c3d07e 100644 --- a/Lib/mailcap.py +++ b/Lib/mailcap.py @@ -8,7 +8,7 @@ import string def getcaps(): """Return a dictionary containing the mailcap database. - + The dictionary maps a MIME type (in all lowercase, e.g. 'text/plain') to a list of dictionaries corresponding to mailcap entries. The list collects all the entries for that MIME type from all available mailcap @@ -137,7 +137,7 @@ def parsefield(line, i, n): def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]): """Find a match for a mailcap entry. - + Return a tuple containing the command line, and the mailcap entry used; (None, None) if no match is found. This may invoke the 'test' command of several matching entries before deciding which @@ -145,7 +145,7 @@ def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]): """ entries = lookup(caps, MIMEtype, key) - # XXX This code should somehow check for the needsterminal flag. + # XXX This code should somehow check for the needsterminal flag. for e in entries: if e.has_key('test'): test = subst(e['test'], filename, plist) diff --git a/Lib/mimetools.py b/Lib/mimetools.py index 630a1c3..bb8a299 100644 --- a/Lib/mimetools.py +++ b/Lib/mimetools.py @@ -7,85 +7,85 @@ import tempfile class Message(rfc822.Message): - """A derived class of rfc822.Message that knows about MIME headers and - contains some hooks for decoding encoded and multipart messages.""" - - def __init__(self, fp, seekable = 1): - rfc822.Message.__init__(self, fp, seekable) - self.encodingheader = \ - self.getheader('content-transfer-encoding') - self.typeheader = \ - self.getheader('content-type') - self.parsetype() - self.parseplist() - - def parsetype(self): - str = self.typeheader - if str is None: - str = 'text/plain' - if ';' in str: - i = str.index(';') - self.plisttext = str[i:] - str = str[:i] - else: - self.plisttext = '' - fields = str.split('/') - for i in range(len(fields)): - fields[i] = fields[i].strip().lower() - self.type = '/'.join(fields) - self.maintype = fields[0] - self.subtype = '/'.join(fields[1:]) - - def parseplist(self): - str = self.plisttext - self.plist = [] - while str[:1] == ';': - str = str[1:] - if ';' in str: - # XXX Should parse quotes! - end = str.index(';') - else: - end = len(str) - f = str[:end] - if '=' in f: - i = f.index('=') - f = f[:i].strip().lower() + \ - '=' + f[i+1:].strip() - self.plist.append(f.strip()) - str = str[end:] - - def getplist(self): - return self.plist - - def getparam(self, name): - name = name.lower() + '=' - n = len(name) - for p in self.plist: - if p[:n] == name: - return rfc822.unquote(p[n:]) - return None - - def getparamnames(self): - result = [] - for p in self.plist: - i = p.find('=') - if i >= 0: - result.append(p[:i].lower()) - return result - - def getencoding(self): - if self.encodingheader is None: - return '7bit' - return self.encodingheader.lower() - - def gettype(self): - return self.type - - def getmaintype(self): - return self.maintype - - def getsubtype(self): - return self.subtype + """A derived class of rfc822.Message that knows about MIME headers and + contains some hooks for decoding encoded and multipart messages.""" + + def __init__(self, fp, seekable = 1): + rfc822.Message.__init__(self, fp, seekable) + self.encodingheader = \ + self.getheader('content-transfer-encoding') + self.typeheader = \ + self.getheader('content-type') + self.parsetype() + self.parseplist() + + def parsetype(self): + str = self.typeheader + if str is None: + str = 'text/plain' + if ';' in str: + i = str.index(';') + self.plisttext = str[i:] + str = str[:i] + else: + self.plisttext = '' + fields = str.split('/') + for i in range(len(fields)): + fields[i] = fields[i].strip().lower() + self.type = '/'.join(fields) + self.maintype = fields[0] + self.subtype = '/'.join(fields[1:]) + + def parseplist(self): + str = self.plisttext + self.plist = [] + while str[:1] == ';': + str = str[1:] + if ';' in str: + # XXX Should parse quotes! + end = str.index(';') + else: + end = len(str) + f = str[:end] + if '=' in f: + i = f.index('=') + f = f[:i].strip().lower() + \ + '=' + f[i+1:].strip() + self.plist.append(f.strip()) + str = str[end:] + + def getplist(self): + return self.plist + + def getparam(self, name): + name = name.lower() + '=' + n = len(name) + for p in self.plist: + if p[:n] == name: + return rfc822.unquote(p[n:]) + return None + + def getparamnames(self): + result = [] + for p in self.plist: + i = p.find('=') + if i >= 0: + result.append(p[:i].lower()) + return result + + def getencoding(self): + if self.encodingheader is None: + return '7bit' + return self.encodingheader.lower() + + def gettype(self): + return self.type + + def getmaintype(self): + return self.maintype + + def getsubtype(self): + return self.subtype @@ -97,74 +97,74 @@ class Message(rfc822.Message): _prefix = None def choose_boundary(): - """Return a random string usable as a multipart boundary. - The method used is so that it is *very* unlikely that the same - string of characters will every occur again in the Universe, - so the caller needn't check the data it is packing for the - occurrence of the boundary. - - The boundary contains dots so you have to quote it in the header.""" - - global _prefix - import time - import random - if _prefix is None: - import socket - import os - hostid = socket.gethostbyname(socket.gethostname()) - try: - uid = `os.getuid()` - except: - uid = '1' - try: - pid = `os.getpid()` - except: - pid = '1' - _prefix = hostid + '.' + uid + '.' + pid - timestamp = '%.3f' % time.time() - seed = `random.randint(0, 32767)` - return _prefix + '.' + timestamp + '.' + seed + """Return a random string usable as a multipart boundary. + The method used is so that it is *very* unlikely that the same + string of characters will every occur again in the Universe, + so the caller needn't check the data it is packing for the + occurrence of the boundary. + + The boundary contains dots so you have to quote it in the header.""" + + global _prefix + import time + import random + if _prefix is None: + import socket + import os + hostid = socket.gethostbyname(socket.gethostname()) + try: + uid = `os.getuid()` + except: + uid = '1' + try: + pid = `os.getpid()` + except: + pid = '1' + _prefix = hostid + '.' + uid + '.' + pid + timestamp = '%.3f' % time.time() + seed = `random.randint(0, 32767)` + return _prefix + '.' + timestamp + '.' + seed # Subroutines for decoding some common content-transfer-types def decode(input, output, encoding): - """Decode common content-transfer-encodings (base64, quopri, uuencode).""" - if encoding == 'base64': - import base64 - return base64.decode(input, output) - if encoding == 'quoted-printable': - import quopri - return quopri.decode(input, output) - if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'): - import uu - return uu.decode(input, output) - if encoding in ('7bit', '8bit'): - return output.write(input.read()) - if decodetab.has_key(encoding): - pipethrough(input, decodetab[encoding], output) - else: - raise ValueError, \ - 'unknown Content-Transfer-Encoding: %s' % encoding + """Decode common content-transfer-encodings (base64, quopri, uuencode).""" + if encoding == 'base64': + import base64 + return base64.decode(input, output) + if encoding == 'quoted-printable': + import quopri + return quopri.decode(input, output) + if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'): + import uu + return uu.decode(input, output) + if encoding in ('7bit', '8bit'): + return output.write(input.read()) + if decodetab.has_key(encoding): + pipethrough(input, decodetab[encoding], output) + else: + raise ValueError, \ + 'unknown Content-Transfer-Encoding: %s' % encoding def encode(input, output, encoding): - """Encode common content-transfer-encodings (base64, quopri, uuencode).""" - if encoding == 'base64': - import base64 - return base64.encode(input, output) - if encoding == 'quoted-printable': - import quopri - return quopri.encode(input, output, 0) - if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'): - import uu - return uu.encode(input, output) - if encoding in ('7bit', '8bit'): - return output.write(input.read()) - if encodetab.has_key(encoding): - pipethrough(input, encodetab[encoding], output) - else: - raise ValueError, \ - 'unknown Content-Transfer-Encoding: %s' % encoding + """Encode common content-transfer-encodings (base64, quopri, uuencode).""" + if encoding == 'base64': + import base64 + return base64.encode(input, output) + if encoding == 'quoted-printable': + import quopri + return quopri.encode(input, output, 0) + if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'): + import uu + return uu.encode(input, output) + if encoding in ('7bit', '8bit'): + return output.write(input.read()) + if encodetab.has_key(encoding): + pipethrough(input, encodetab[encoding], output) + else: + raise ValueError, \ + 'unknown Content-Transfer-Encoding: %s' % encoding # The following is no longer used for standard encodings @@ -178,51 +178,51 @@ rm $TEMP )''' decodetab = { - 'uuencode': uudecode_pipe, - 'x-uuencode': uudecode_pipe, - 'uue': uudecode_pipe, - 'x-uue': uudecode_pipe, - 'quoted-printable': 'mmencode -u -q', - 'base64': 'mmencode -u -b', + 'uuencode': uudecode_pipe, + 'x-uuencode': uudecode_pipe, + 'uue': uudecode_pipe, + 'x-uue': uudecode_pipe, + 'quoted-printable': 'mmencode -u -q', + 'base64': 'mmencode -u -b', } encodetab = { - 'x-uuencode': 'uuencode tempfile', - 'uuencode': 'uuencode tempfile', - 'x-uue': 'uuencode tempfile', - 'uue': 'uuencode tempfile', - 'quoted-printable': 'mmencode -q', - 'base64': 'mmencode -b', + 'x-uuencode': 'uuencode tempfile', + 'uuencode': 'uuencode tempfile', + 'x-uue': 'uuencode tempfile', + 'uue': 'uuencode tempfile', + 'quoted-printable': 'mmencode -q', + 'base64': 'mmencode -b', } def pipeto(input, command): - pipe = os.popen(command, 'w') - copyliteral(input, pipe) - pipe.close() + pipe = os.popen(command, 'w') + copyliteral(input, pipe) + pipe.close() def pipethrough(input, command, output): - tempname = tempfile.mktemp() - try: - temp = open(tempname, 'w') - except IOError: - print '*** Cannot create temp file', `tempname` - return - copyliteral(input, temp) - temp.close() - pipe = os.popen(command + ' <' + tempname, 'r') - copybinary(pipe, output) - pipe.close() - os.unlink(tempname) + tempname = tempfile.mktemp() + try: + temp = open(tempname, 'w') + except IOError: + print '*** Cannot create temp file', `tempname` + return + copyliteral(input, temp) + temp.close() + pipe = os.popen(command + ' <' + tempname, 'r') + copybinary(pipe, output) + pipe.close() + os.unlink(tempname) def copyliteral(input, output): - while 1: - line = input.readline() - if not line: break - output.write(line) + while 1: + line = input.readline() + if not line: break + output.write(line) def copybinary(input, output): - BUFSIZE = 8192 - while 1: - line = input.read(BUFSIZE) - if not line: break - output.write(line) + BUFSIZE = 8192 + while 1: + line = input.read(BUFSIZE) + if not line: break + output.write(line) diff --git a/Lib/mimify.py b/Lib/mimify.py index 20b4d6c..34b0206 100755 --- a/Lib/mimify.py +++ b/Lib/mimify.py @@ -6,8 +6,8 @@ Decode quoted-printable parts of a mail message or encode using quoted-printable. Usage: - mimify(input, output) - unmimify(input, output, decode_base64 = 0) + mimify(input, output) + unmimify(input, output, decode_base64 = 0) to encode and decode respectively. Input and output may be the name of a file or an open file object. Only a readline() method is used on the input file, only a write() method is used on the output file. @@ -15,16 +15,16 @@ When using file names, the input and output file names may be the same. Interactive usage: - mimify.py -e [infile [outfile]] - mimify.py -d [infile [outfile]] + mimify.py -e [infile [outfile]] + mimify.py -d [infile [outfile]] to encode and decode respectively. Infile defaults to standard input and outfile to standard output. """ # Configure -MAXLEN = 200 # if lines longer than this, encode as quoted-printable -CHARSET = 'ISO-8859-1' # default charset for non-US-ASCII mail -QUOTE = '> ' # string replies are quoted with +MAXLEN = 200 # if lines longer than this, encode as quoted-printable +CHARSET = 'ISO-8859-1' # default charset for non-US-ASCII mail +QUOTE = '> ' # string replies are quoted with # End configure import re, string @@ -39,425 +39,424 @@ mime_head = re.compile('=\\?iso-8859-1\\?q\\?([^? \t\n]+)\\?=', re.I) repl = re.compile('^subject:\\s+re: ', re.I) class File: - """A simple fake file object that knows about limited read-ahead and - boundaries. The only supported method is readline().""" - - def __init__(self, file, boundary): - self.file = file - self.boundary = boundary - self.peek = None - - def readline(self): - if self.peek is not None: - return '' - line = self.file.readline() - if not line: - return line - if self.boundary: - if line == self.boundary + '\n': - self.peek = line - return '' - if line == self.boundary + '--\n': - self.peek = line - return '' - return line + """A simple fake file object that knows about limited read-ahead and + boundaries. The only supported method is readline().""" + + def __init__(self, file, boundary): + self.file = file + self.boundary = boundary + self.peek = None + + def readline(self): + if self.peek is not None: + return '' + line = self.file.readline() + if not line: + return line + if self.boundary: + if line == self.boundary + '\n': + self.peek = line + return '' + if line == self.boundary + '--\n': + self.peek = line + return '' + return line class HeaderFile: - def __init__(self, file): - self.file = file - self.peek = None - - def readline(self): - if self.peek is not None: - line = self.peek - self.peek = None - else: - line = self.file.readline() - if not line: - return line - if he.match(line): - return line - while 1: - self.peek = self.file.readline() - if len(self.peek) == 0 or \ - (self.peek[0] != ' ' and self.peek[0] != '\t'): - return line - line = line + self.peek - self.peek = None + def __init__(self, file): + self.file = file + self.peek = None + + def readline(self): + if self.peek is not None: + line = self.peek + self.peek = None + else: + line = self.file.readline() + if not line: + return line + if he.match(line): + return line + while 1: + self.peek = self.file.readline() + if len(self.peek) == 0 or \ + (self.peek[0] != ' ' and self.peek[0] != '\t'): + return line + line = line + self.peek + self.peek = None def mime_decode(line): - """Decode a single line of quoted-printable text to 8bit.""" - newline = '' - pos = 0 - while 1: - res = mime_code.search(line, pos) - if res is None: - break - newline = newline + line[pos:res.start(0)] + \ - chr(string.atoi(res.group(1), 16)) - pos = res.end(0) - return newline + line[pos:] + """Decode a single line of quoted-printable text to 8bit.""" + newline = '' + pos = 0 + while 1: + res = mime_code.search(line, pos) + if res is None: + break + newline = newline + line[pos:res.start(0)] + \ + chr(string.atoi(res.group(1), 16)) + pos = res.end(0) + return newline + line[pos:] def mime_decode_header(line): - """Decode a header line to 8bit.""" - newline = '' - pos = 0 - while 1: - res = mime_head.search(line, pos) - if res is None: - break - match = res.group(1) - # convert underscores to spaces (before =XX conversion!) - match = string.join(string.split(match, '_'), ' ') - newline = newline + line[pos:res.start(0)] + mime_decode(match) - pos = res.end(0) - return newline + line[pos:] + """Decode a header line to 8bit.""" + newline = '' + pos = 0 + while 1: + res = mime_head.search(line, pos) + if res is None: + break + match = res.group(1) + # convert underscores to spaces (before =XX conversion!) + match = string.join(string.split(match, '_'), ' ') + newline = newline + line[pos:res.start(0)] + mime_decode(match) + pos = res.end(0) + return newline + line[pos:] def unmimify_part(ifile, ofile, decode_base64 = 0): - """Convert a quoted-printable part of a MIME mail message to 8bit.""" - multipart = None - quoted_printable = 0 - is_base64 = 0 - is_repl = 0 - if ifile.boundary and ifile.boundary[:2] == QUOTE: - prefix = QUOTE - else: - prefix = '' - - # read header - hfile = HeaderFile(ifile) - while 1: - line = hfile.readline() - if not line: - return - if prefix and line[:len(prefix)] == prefix: - line = line[len(prefix):] - pref = prefix - else: - pref = '' - line = mime_decode_header(line) - if qp.match(line): - quoted_printable = 1 - continue # skip this header - if decode_base64 and base64_re.match(line): - is_base64 = 1 - continue - ofile.write(pref + line) - if not prefix and repl.match(line): - # we're dealing with a reply message - is_repl = 1 - mp_res = mp.match(line) - if mp_res: - multipart = '--' + mp_res.group(1) - if he.match(line): - break - if is_repl and (quoted_printable or multipart): - is_repl = 0 - - # read body - while 1: - line = ifile.readline() - if not line: - return - line = re.sub(mime_head, '\\1', line) - if prefix and line[:len(prefix)] == prefix: - line = line[len(prefix):] - pref = prefix - else: - pref = '' -## if is_repl and len(line) >= 4 and line[:4] == QUOTE+'--' and line[-3:] != '--\n': -## multipart = line[:-1] - while multipart: - if line == multipart + '--\n': - ofile.write(pref + line) - multipart = None - line = None - break - if line == multipart + '\n': - ofile.write(pref + line) - nifile = File(ifile, multipart) - unmimify_part(nifile, ofile, decode_base64) - line = nifile.peek - if not line: - # premature end of file - break - continue - # not a boundary between parts - break - if line and quoted_printable: - while line[-2:] == '=\n': - line = line[:-2] - newline = ifile.readline() - if newline[:len(QUOTE)] == QUOTE: - newline = newline[len(QUOTE):] - line = line + newline - line = mime_decode(line) - if line and is_base64 and not pref: - import base64 - line = base64.decodestring(line) - if line: - ofile.write(pref + line) + """Convert a quoted-printable part of a MIME mail message to 8bit.""" + multipart = None + quoted_printable = 0 + is_base64 = 0 + is_repl = 0 + if ifile.boundary and ifile.boundary[:2] == QUOTE: + prefix = QUOTE + else: + prefix = '' + + # read header + hfile = HeaderFile(ifile) + while 1: + line = hfile.readline() + if not line: + return + if prefix and line[:len(prefix)] == prefix: + line = line[len(prefix):] + pref = prefix + else: + pref = '' + line = mime_decode_header(line) + if qp.match(line): + quoted_printable = 1 + continue # skip this header + if decode_base64 and base64_re.match(line): + is_base64 = 1 + continue + ofile.write(pref + line) + if not prefix and repl.match(line): + # we're dealing with a reply message + is_repl = 1 + mp_res = mp.match(line) + if mp_res: + multipart = '--' + mp_res.group(1) + if he.match(line): + break + if is_repl and (quoted_printable or multipart): + is_repl = 0 + + # read body + while 1: + line = ifile.readline() + if not line: + return + line = re.sub(mime_head, '\\1', line) + if prefix and line[:len(prefix)] == prefix: + line = line[len(prefix):] + pref = prefix + else: + pref = '' +## if is_repl and len(line) >= 4 and line[:4] == QUOTE+'--' and line[-3:] != '--\n': +## multipart = line[:-1] + while multipart: + if line == multipart + '--\n': + ofile.write(pref + line) + multipart = None + line = None + break + if line == multipart + '\n': + ofile.write(pref + line) + nifile = File(ifile, multipart) + unmimify_part(nifile, ofile, decode_base64) + line = nifile.peek + if not line: + # premature end of file + break + continue + # not a boundary between parts + break + if line and quoted_printable: + while line[-2:] == '=\n': + line = line[:-2] + newline = ifile.readline() + if newline[:len(QUOTE)] == QUOTE: + newline = newline[len(QUOTE):] + line = line + newline + line = mime_decode(line) + if line and is_base64 and not pref: + import base64 + line = base64.decodestring(line) + if line: + ofile.write(pref + line) def unmimify(infile, outfile, decode_base64 = 0): - """Convert quoted-printable parts of a MIME mail message to 8bit.""" - if type(infile) == type(''): - ifile = open(infile) - if type(outfile) == type('') and infile == outfile: - import os - d, f = os.path.split(infile) - os.rename(infile, os.path.join(d, ',' + f)) - else: - ifile = infile - if type(outfile) == type(''): - ofile = open(outfile, 'w') - else: - ofile = outfile - nifile = File(ifile, None) - unmimify_part(nifile, ofile, decode_base64) - ofile.flush() + """Convert quoted-printable parts of a MIME mail message to 8bit.""" + if type(infile) == type(''): + ifile = open(infile) + if type(outfile) == type('') and infile == outfile: + import os + d, f = os.path.split(infile) + os.rename(infile, os.path.join(d, ',' + f)) + else: + ifile = infile + if type(outfile) == type(''): + ofile = open(outfile, 'w') + else: + ofile = outfile + nifile = File(ifile, None) + unmimify_part(nifile, ofile, decode_base64) + ofile.flush() mime_char = re.compile('[=\177-\377]') # quote these chars in body mime_header_char = re.compile('[=?\177-\377]') # quote these in header def mime_encode(line, header): - """Code a single line as quoted-printable. - If header is set, quote some extra characters.""" - if header: - reg = mime_header_char - else: - reg = mime_char - newline = '' - pos = 0 - if len(line) >= 5 and line[:5] == 'From ': - # quote 'From ' at the start of a line for stupid mailers - newline = string.upper('=%02x' % ord('F')) - pos = 1 - while 1: - res = reg.search(line, pos) - if res is None: - break - newline = newline + line[pos:res.start(0)] + \ - string.upper('=%02x' % ord(res.group(0))) - pos = res.end(0) - line = newline + line[pos:] - - newline = '' - while len(line) >= 75: - i = 73 - while line[i] == '=' or line[i-1] == '=': - i = i - 1 - i = i + 1 - newline = newline + line[:i] + '=\n' - line = line[i:] - return newline + line + """Code a single line as quoted-printable. + If header is set, quote some extra characters.""" + if header: + reg = mime_header_char + else: + reg = mime_char + newline = '' + pos = 0 + if len(line) >= 5 and line[:5] == 'From ': + # quote 'From ' at the start of a line for stupid mailers + newline = string.upper('=%02x' % ord('F')) + pos = 1 + while 1: + res = reg.search(line, pos) + if res is None: + break + newline = newline + line[pos:res.start(0)] + \ + string.upper('=%02x' % ord(res.group(0))) + pos = res.end(0) + line = newline + line[pos:] + + newline = '' + while len(line) >= 75: + i = 73 + while line[i] == '=' or line[i-1] == '=': + i = i - 1 + i = i + 1 + newline = newline + line[:i] + '=\n' + line = line[i:] + return newline + line mime_header = re.compile('([ \t(]|^)([-a-zA-Z0-9_+]*[\177-\377][-a-zA-Z0-9_+\177-\377]*)([ \t)]|\n)') def mime_encode_header(line): - """Code a single header line as quoted-printable.""" - newline = '' - pos = 0 - while 1: - res = mime_header.search(line, pos) - if res is None: - break - newline = '%s%s%s=?%s?Q?%s?=%s' % \ - (newline, line[pos:res.start(0)], res.group(1), - CHARSET, mime_encode(res.group(2), 1), res.group(3)) - pos = res.end(0) - return newline + line[pos:] + """Code a single header line as quoted-printable.""" + newline = '' + pos = 0 + while 1: + res = mime_header.search(line, pos) + if res is None: + break + newline = '%s%s%s=?%s?Q?%s?=%s' % \ + (newline, line[pos:res.start(0)], res.group(1), + CHARSET, mime_encode(res.group(2), 1), res.group(3)) + pos = res.end(0) + return newline + line[pos:] mv = re.compile('^mime-version:', re.I) cte = re.compile('^content-transfer-encoding:', re.I) iso_char = re.compile('[\177-\377]') def mimify_part(ifile, ofile, is_mime): - """Convert an 8bit part of a MIME mail message to quoted-printable.""" - has_cte = is_qp = is_base64 = 0 - multipart = None - must_quote_body = must_quote_header = has_iso_chars = 0 - - header = [] - header_end = '' - message = [] - message_end = '' - # read header - hfile = HeaderFile(ifile) - while 1: - line = hfile.readline() - if not line: - break - if not must_quote_header and iso_char.search(line): - must_quote_header = 1 - if mv.match(line): - is_mime = 1 - if cte.match(line): - has_cte = 1 - if qp.match(line): - is_qp = 1 - elif base64_re.match(line): - is_base64 = 1 - mp_res = mp.match(line) - if mp_res: - multipart = '--' + mp_res.group(1) - if he.match(line): - header_end = line - break - header.append(line) - - # read body - while 1: - line = ifile.readline() - if not line: - break - if multipart: - if line == multipart + '--\n': - message_end = line - break - if line == multipart + '\n': - message_end = line - break - if is_base64: - message.append(line) - continue - if is_qp: - while line[-2:] == '=\n': - line = line[:-2] - newline = ifile.readline() - if newline[:len(QUOTE)] == QUOTE: - newline = newline[len(QUOTE):] - line = line + newline - line = mime_decode(line) - message.append(line) - if not has_iso_chars: - if iso_char.search(line): - has_iso_chars = must_quote_body = 1 - if not must_quote_body: - if len(line) > MAXLEN: - must_quote_body = 1 - - # convert and output header and body - for line in header: - if must_quote_header: - line = mime_encode_header(line) - chrset_res = chrset.match(line) - if chrset_res: - if has_iso_chars: - # change us-ascii into iso-8859-1 - if string.lower(chrset_res.group(2)) == 'us-ascii': - line = '%s%s%s' % (chrset_res.group(1), - CHARSET, - chrset_res.group(3)) - else: - # change iso-8859-* into us-ascii - line = '%sus-ascii%s' % chrset_res.group(1, 3) - if has_cte and cte.match(line): - line = 'Content-Transfer-Encoding: ' - if is_base64: - line = line + 'base64\n' - elif must_quote_body: - line = line + 'quoted-printable\n' - else: - line = line + '7bit\n' - ofile.write(line) - if (must_quote_header or must_quote_body) and not is_mime: - ofile.write('Mime-Version: 1.0\n') - ofile.write('Content-Type: text/plain; ') - if has_iso_chars: - ofile.write('charset="%s"\n' % CHARSET) - else: - ofile.write('charset="us-ascii"\n') - if must_quote_body and not has_cte: - ofile.write('Content-Transfer-Encoding: quoted-printable\n') - ofile.write(header_end) - - for line in message: - if must_quote_body: - line = mime_encode(line, 0) - ofile.write(line) - ofile.write(message_end) - - line = message_end - while multipart: - if line == multipart + '--\n': - # read bit after the end of the last part - while 1: - line = ifile.readline() - if not line: - return - if must_quote_body: - line = mime_encode(line, 0) - ofile.write(line) - if line == multipart + '\n': - nifile = File(ifile, multipart) - mimify_part(nifile, ofile, 1) - line = nifile.peek - if not line: - # premature end of file - break - ofile.write(line) - continue - # unexpectedly no multipart separator--copy rest of file - while 1: - line = ifile.readline() - if not line: - return - if must_quote_body: - line = mime_encode(line, 0) - ofile.write(line) + """Convert an 8bit part of a MIME mail message to quoted-printable.""" + has_cte = is_qp = is_base64 = 0 + multipart = None + must_quote_body = must_quote_header = has_iso_chars = 0 + + header = [] + header_end = '' + message = [] + message_end = '' + # read header + hfile = HeaderFile(ifile) + while 1: + line = hfile.readline() + if not line: + break + if not must_quote_header and iso_char.search(line): + must_quote_header = 1 + if mv.match(line): + is_mime = 1 + if cte.match(line): + has_cte = 1 + if qp.match(line): + is_qp = 1 + elif base64_re.match(line): + is_base64 = 1 + mp_res = mp.match(line) + if mp_res: + multipart = '--' + mp_res.group(1) + if he.match(line): + header_end = line + break + header.append(line) + + # read body + while 1: + line = ifile.readline() + if not line: + break + if multipart: + if line == multipart + '--\n': + message_end = line + break + if line == multipart + '\n': + message_end = line + break + if is_base64: + message.append(line) + continue + if is_qp: + while line[-2:] == '=\n': + line = line[:-2] + newline = ifile.readline() + if newline[:len(QUOTE)] == QUOTE: + newline = newline[len(QUOTE):] + line = line + newline + line = mime_decode(line) + message.append(line) + if not has_iso_chars: + if iso_char.search(line): + has_iso_chars = must_quote_body = 1 + if not must_quote_body: + if len(line) > MAXLEN: + must_quote_body = 1 + + # convert and output header and body + for line in header: + if must_quote_header: + line = mime_encode_header(line) + chrset_res = chrset.match(line) + if chrset_res: + if has_iso_chars: + # change us-ascii into iso-8859-1 + if string.lower(chrset_res.group(2)) == 'us-ascii': + line = '%s%s%s' % (chrset_res.group(1), + CHARSET, + chrset_res.group(3)) + else: + # change iso-8859-* into us-ascii + line = '%sus-ascii%s' % chrset_res.group(1, 3) + if has_cte and cte.match(line): + line = 'Content-Transfer-Encoding: ' + if is_base64: + line = line + 'base64\n' + elif must_quote_body: + line = line + 'quoted-printable\n' + else: + line = line + '7bit\n' + ofile.write(line) + if (must_quote_header or must_quote_body) and not is_mime: + ofile.write('Mime-Version: 1.0\n') + ofile.write('Content-Type: text/plain; ') + if has_iso_chars: + ofile.write('charset="%s"\n' % CHARSET) + else: + ofile.write('charset="us-ascii"\n') + if must_quote_body and not has_cte: + ofile.write('Content-Transfer-Encoding: quoted-printable\n') + ofile.write(header_end) + + for line in message: + if must_quote_body: + line = mime_encode(line, 0) + ofile.write(line) + ofile.write(message_end) + + line = message_end + while multipart: + if line == multipart + '--\n': + # read bit after the end of the last part + while 1: + line = ifile.readline() + if not line: + return + if must_quote_body: + line = mime_encode(line, 0) + ofile.write(line) + if line == multipart + '\n': + nifile = File(ifile, multipart) + mimify_part(nifile, ofile, 1) + line = nifile.peek + if not line: + # premature end of file + break + ofile.write(line) + continue + # unexpectedly no multipart separator--copy rest of file + while 1: + line = ifile.readline() + if not line: + return + if must_quote_body: + line = mime_encode(line, 0) + ofile.write(line) def mimify(infile, outfile): - """Convert 8bit parts of a MIME mail message to quoted-printable.""" - if type(infile) == type(''): - ifile = open(infile) - if type(outfile) == type('') and infile == outfile: - import os - d, f = os.path.split(infile) - os.rename(infile, os.path.join(d, ',' + f)) - else: - ifile = infile - if type(outfile) == type(''): - ofile = open(outfile, 'w') - else: - ofile = outfile - nifile = File(ifile, None) - mimify_part(nifile, ofile, 0) - ofile.flush() + """Convert 8bit parts of a MIME mail message to quoted-printable.""" + if type(infile) == type(''): + ifile = open(infile) + if type(outfile) == type('') and infile == outfile: + import os + d, f = os.path.split(infile) + os.rename(infile, os.path.join(d, ',' + f)) + else: + ifile = infile + if type(outfile) == type(''): + ofile = open(outfile, 'w') + else: + ofile = outfile + nifile = File(ifile, None) + mimify_part(nifile, ofile, 0) + ofile.flush() import sys if __name__ == '__main__' or (len(sys.argv) > 0 and sys.argv[0] == 'mimify'): - import getopt - usage = 'Usage: mimify [-l len] -[ed] [infile [outfile]]' - - decode_base64 = 0 - opts, args = getopt.getopt(sys.argv[1:], 'l:edb') - if len(args) not in (0, 1, 2): - print usage - sys.exit(1) - if (('-e', '') in opts) == (('-d', '') in opts) or \ - ((('-b', '') in opts) and (('-d', '') not in opts)): - print usage - sys.exit(1) - for o, a in opts: - if o == '-e': - encode = mimify - elif o == '-d': - encode = unmimify - elif o == '-l': - try: - MAXLEN = string.atoi(a) - except: - print usage - sys.exit(1) - elif o == '-b': - decode_base64 = 1 - if len(args) == 0: - encode_args = (sys.stdin, sys.stdout) - elif len(args) == 1: - encode_args = (args[0], sys.stdout) - else: - encode_args = (args[0], args[1]) - if decode_base64: - encode_args = encode_args + (decode_base64,) - apply(encode, encode_args) - + import getopt + usage = 'Usage: mimify [-l len] -[ed] [infile [outfile]]' + + decode_base64 = 0 + opts, args = getopt.getopt(sys.argv[1:], 'l:edb') + if len(args) not in (0, 1, 2): + print usage + sys.exit(1) + if (('-e', '') in opts) == (('-d', '') in opts) or \ + ((('-b', '') in opts) and (('-d', '') not in opts)): + print usage + sys.exit(1) + for o, a in opts: + if o == '-e': + encode = mimify + elif o == '-d': + encode = unmimify + elif o == '-l': + try: + MAXLEN = string.atoi(a) + except: + print usage + sys.exit(1) + elif o == '-b': + decode_base64 = 1 + if len(args) == 0: + encode_args = (sys.stdin, sys.stdout) + elif len(args) == 1: + encode_args = (args[0], sys.stdout) + else: + encode_args = (args[0], args[1]) + if decode_base64: + encode_args = encode_args + (decode_base64,) + apply(encode, encode_args) diff --git a/Lib/multifile.py b/Lib/multifile.py index 0b51b55..38a5a46 100644 --- a/Lib/multifile.py +++ b/Lib/multifile.py @@ -13,8 +13,8 @@ fp = MultiFile(real_fp) "read some lines from fp" fp.push(separator) while 1: - "read lines from fp until it returns an empty string" (A) - if not fp.next(): break + "read lines from fp until it returns an empty string" (A) + if not fp.next(): break fp.pop() "read remaining lines from fp until it returns an empty string" @@ -31,134 +31,134 @@ import sys import string class Error(Exception): - pass + pass class MultiFile: - seekable = 0 - - def __init__(self, fp, seekable=1): - self.fp = fp - self.stack = [] # Grows down - self.level = 0 - self.last = 0 - if seekable: - self.seekable = 1 - self.start = self.fp.tell() - self.posstack = [] # Grows down - - def tell(self): - if self.level > 0: - return self.lastpos - return self.fp.tell() - self.start - - def seek(self, pos, whence=0): - here = self.tell() - if whence: - if whence == 1: - pos = pos + here - elif whence == 2: - if self.level > 0: - pos = pos + self.lastpos - else: - raise Error, "can't use whence=2 yet" - if not 0 <= pos <= here or \ - self.level > 0 and pos > self.lastpos: - raise Error, 'bad MultiFile.seek() call' - self.fp.seek(pos + self.start) - self.level = 0 - self.last = 0 - - def readline(self): - if self.level > 0: - return '' - line = self.fp.readline() - # Real EOF? - if not line: - self.level = len(self.stack) - self.last = (self.level > 0) - if self.last: - raise Error, 'sudden EOF in MultiFile.readline()' - return '' - assert self.level == 0 - # Fast check to see if this is just data - if self.is_data(line): - return line - else: - # Ignore trailing whitespace on marker lines - k = len(line) - 1 - while line[k] in string.whitespace: - k = k - 1 - marker = line[:k+1] - # No? OK, try to match a boundary. - # Return the line (unstripped) if we don't. - for i in range(len(self.stack)): - sep = self.stack[i] - if marker == self.section_divider(sep): - self.last = 0 - break - elif marker == self.end_marker(sep): - self.last = 1 - break - else: - return line - # We only get here if we see a section divider or EOM line - if self.seekable: - self.lastpos = self.tell() - len(line) - self.level = i+1 - if self.level > 1: - raise Error,'Missing endmarker in MultiFile.readline()' - return '' - - def readlines(self): - list = [] - while 1: - line = self.readline() - if not line: break - list.append(line) - return list - - def read(self): # Note: no size argument -- read until EOF only! - return string.joinfields(self.readlines(), '') - - def next(self): - while self.readline(): pass - if self.level > 1 or self.last: - return 0 - self.level = 0 - self.last = 0 - if self.seekable: - self.start = self.fp.tell() - return 1 - - def push(self, sep): - if self.level > 0: - raise Error, 'bad MultiFile.push() call' - self.stack.insert(0, sep) - if self.seekable: - self.posstack.insert(0, self.start) - self.start = self.fp.tell() - - def pop(self): - if self.stack == []: - raise Error, 'bad MultiFile.pop() call' - if self.level <= 1: - self.last = 0 - else: - abslastpos = self.lastpos + self.start - self.level = max(0, self.level - 1) - del self.stack[0] - if self.seekable: - self.start = self.posstack[0] - del self.posstack[0] - if self.level > 0: - self.lastpos = abslastpos - self.start - - def is_data(self, line): - return line[:2] != '--' - - def section_divider(self, str): - return "--" + str - - def end_marker(self, str): - return "--" + str + "--" + seekable = 0 + + def __init__(self, fp, seekable=1): + self.fp = fp + self.stack = [] # Grows down + self.level = 0 + self.last = 0 + if seekable: + self.seekable = 1 + self.start = self.fp.tell() + self.posstack = [] # Grows down + + def tell(self): + if self.level > 0: + return self.lastpos + return self.fp.tell() - self.start + + def seek(self, pos, whence=0): + here = self.tell() + if whence: + if whence == 1: + pos = pos + here + elif whence == 2: + if self.level > 0: + pos = pos + self.lastpos + else: + raise Error, "can't use whence=2 yet" + if not 0 <= pos <= here or \ + self.level > 0 and pos > self.lastpos: + raise Error, 'bad MultiFile.seek() call' + self.fp.seek(pos + self.start) + self.level = 0 + self.last = 0 + + def readline(self): + if self.level > 0: + return '' + line = self.fp.readline() + # Real EOF? + if not line: + self.level = len(self.stack) + self.last = (self.level > 0) + if self.last: + raise Error, 'sudden EOF in MultiFile.readline()' + return '' + assert self.level == 0 + # Fast check to see if this is just data + if self.is_data(line): + return line + else: + # Ignore trailing whitespace on marker lines + k = len(line) - 1 + while line[k] in string.whitespace: + k = k - 1 + marker = line[:k+1] + # No? OK, try to match a boundary. + # Return the line (unstripped) if we don't. + for i in range(len(self.stack)): + sep = self.stack[i] + if marker == self.section_divider(sep): + self.last = 0 + break + elif marker == self.end_marker(sep): + self.last = 1 + break + else: + return line + # We only get here if we see a section divider or EOM line + if self.seekable: + self.lastpos = self.tell() - len(line) + self.level = i+1 + if self.level > 1: + raise Error,'Missing endmarker in MultiFile.readline()' + return '' + + def readlines(self): + list = [] + while 1: + line = self.readline() + if not line: break + list.append(line) + return list + + def read(self): # Note: no size argument -- read until EOF only! + return string.joinfields(self.readlines(), '') + + def next(self): + while self.readline(): pass + if self.level > 1 or self.last: + return 0 + self.level = 0 + self.last = 0 + if self.seekable: + self.start = self.fp.tell() + return 1 + + def push(self, sep): + if self.level > 0: + raise Error, 'bad MultiFile.push() call' + self.stack.insert(0, sep) + if self.seekable: + self.posstack.insert(0, self.start) + self.start = self.fp.tell() + + def pop(self): + if self.stack == []: + raise Error, 'bad MultiFile.pop() call' + if self.level <= 1: + self.last = 0 + else: + abslastpos = self.lastpos + self.start + self.level = max(0, self.level - 1) + del self.stack[0] + if self.seekable: + self.start = self.posstack[0] + del self.posstack[0] + if self.level > 0: + self.lastpos = abslastpos - self.start + + def is_data(self, line): + return line[:2] != '--' + + def section_divider(self, str): + return "--" + str + + def end_marker(self, str): + return "--" + str + "--" diff --git a/Lib/mutex.py b/Lib/mutex.py index 9271d34..2348a2e 100644 --- a/Lib/mutex.py +++ b/Lib/mutex.py @@ -13,39 +13,39 @@ for lock, where a function is called once the lock is aquired. """ class mutex: - def __init__(self): - """Create a new mutex -- initially unlocked.""" - self.locked = 0 - self.queue = [] + def __init__(self): + """Create a new mutex -- initially unlocked.""" + self.locked = 0 + self.queue = [] - def test(self): - """Test the locked bit of the mutex.""" - return self.locked + def test(self): + """Test the locked bit of the mutex.""" + return self.locked - def testandset(self): - """Atomic test-and-set -- grab the lock if it is not set, - return true if it succeeded.""" - if not self.locked: - self.locked = 1 - return 1 - else: - return 0 + def testandset(self): + """Atomic test-and-set -- grab the lock if it is not set, + return true if it succeeded.""" + if not self.locked: + self.locked = 1 + return 1 + else: + return 0 - def lock(self, function, argument): - """Lock a mutex, call the function with supplied argument - when it is acquired. If the mutex is already locked, place - function and argument in the queue.""" - if self.testandset(): - function(argument) - else: - self.queue.append((function, argument)) + def lock(self, function, argument): + """Lock a mutex, call the function with supplied argument + when it is acquired. If the mutex is already locked, place + function and argument in the queue.""" + if self.testandset(): + function(argument) + else: + self.queue.append((function, argument)) - def unlock(self): - """Unlock a mutex. If the queue is not empty, call the next - function with its argument.""" - if self.queue: - function, argument = self.queue[0] - del self.queue[0] - function(argument) - else: - self.locked = 0 + def unlock(self): + """Unlock a mutex. If the queue is not empty, call the next + function with its argument.""" + if self.queue: + function, argument = self.queue[0] + del self.queue[0] + function(argument) + else: + self.locked = 0 -- cgit v0.12