Changeset 243:35fc2ded237d

Show
Ignore:
Timestamp:
19/02/11 23:37:20 (15 months ago)
Author:
Menno Smits <menno@…>
Branch:
default
Parents:
240:ca209b0b5f0c (diff), 242:295f2c9d5b4f (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

resynced with trunk

Files:
1 removed
2 modified

Legend:

Unmodified
Added
Removed
  • imapclient/imapclient.py

    r240 r243  
    1 # Copyright (c) 2010, Menno Smits 
     1# Copyright (c) 2011, Menno Smits 
    22# Released subject to the New BSD License 
    33# Please see http://en.wikipedia.org/wiki/BSD_licenses 
     
    66import imaplib 
    77import response_lexer 
     8from operator import itemgetter 
     9import warnings 
    810#imaplib.Debug = 5 
    911 
     
    3032RECENT = r'\Recent'         # This flag is read-only 
    3133 
     34class Namespace(tuple): 
     35    def __new__(cls, personal, other, shared): 
     36        return tuple.__new__(cls, (personal, other, shared))  
     37 
     38    personal = property(itemgetter(0)) 
     39    other = property(itemgetter(1)) 
     40    shared = property(itemgetter(2)) 
     41 
    3242 
    3343class IMAPClient(object): 
     
    4959    ReadOnlyError = imaplib.IMAP4.readonly 
    5060 
    51     re_sep = re.compile('^\(\("[^"]*" "([^"]+)"\)\)') 
    5261    re_status = re.compile(r'^\s*"?(?P<folder>[^"]+)"?\s+' 
    5362                           r'\((?P<status_items>.*)\)$') 
     
    105114            return False 
    106115 
    107     def get_folder_delimiter(self): 
    108         """Return the folder separator used by the IMAP server (eg. "/"). 
     116    def namespace(self): 
     117        """Return the namespace for the account as a (personal, other, shared) tuple. 
     118 
     119        Each element may be None if no namespace of that type exists, 
     120        or a sequence of (prefix, separator) pairs. 
     121 
     122        For convenience the tuple elements may be accessed 
     123        positionally or attributes named "personal", "other" and 
     124        "shared". 
     125 
     126        See RFC 2342 for more details. 
    109127        """ 
    110128        typ, data = self._imap.namespace() 
    111129        self._checkok('namespace', typ, data) 
    112  
    113         match = self.re_sep.match(data[0]) 
    114         if match: 
    115             return match.group(1) 
    116         else: 
    117             raise self.Error('could not determine folder separator') 
     130        return Namespace(*parse_response(data)) 
     131 
     132    def get_folder_delimiter(self): 
     133        """Determine the folder separator used by the IMAP server. 
     134 
     135        WARNING: The implementation just picks the first folder 
     136        separator from the first namespace returned. This is not 
     137        particularly sensible. Use namespace instead(). 
     138 
     139        @return: The folder separator. 
     140        @rtype: string 
     141        """ 
     142        warnings.warn(DeprecationWarning('get_folder_delimiter is going away. Use namespace() instead.')) 
     143        for part in self.namespace(): 
     144            for ns in part: 
     145                return ns[1] 
     146        raise self.Error('could not determine folder separator') 
    118147 
    119148    def list_folders(self, directory="", pattern="*"): 
     
    415444 
    416445 
    417     def fetch(self, messages, parts): 
     446    def fetch(self, messages, parts, modifiers=None): 
    418447        """Retrieve selected data items for one or more messages. 
    419448 
    420449        @param messages: Message IDs to fetch. 
    421450        @param parts: A sequence of data items to retrieve. 
     451        @param modifiers: An optional sequence of modifiers (where 
     452            supported by the server, eg. ['CHANGEDSINCE 123']). 
    422453        @return: A dictionary indexed by message number. Each item is itself a 
    423454            dictionary containing the requested message parts. 
     
    430461        msg_list = messages_to_str(messages) 
    431462        parts_list = seq_to_parenlist([p.upper() for p in parts]) 
     463        modifiers_list = None 
     464        if modifiers is not None: 
     465          modifiers_list = seq_to_parenlist([m.upper() for m in modifiers]) 
    432466 
    433467        if self.use_uid: 
    434             tag = self._imap._command('UID', 'FETCH', msg_list, parts_list) 
    435         else: 
    436             tag = self._imap._command('FETCH', msg_list, parts_list) 
     468            tag = self._imap._command('UID', 'FETCH', msg_list, parts_list, modifiers_list) 
     469        else: 
     470            tag = self._imap._command('FETCH', msg_list, parts_list, modifiers_list) 
    437471        typ, data = self._imap._command_complete('FETCH', tag) 
    438472        self._checkok('fetch', typ, data) 
    439473        typ, data = self._imap._untagged_response(typ, data, 'FETCH') 
    440         # appears to be a special case - no 'untagged' responses (ie, no 
    441         # folders) results in [None] 
    442         if data == [None]: 
    443           return {} 
    444  
    445474        return parse_fetch_response(data) 
    446475 
     
    555584            typ, data = self._imap.store(msg_list, cmd, flag_list) 
    556585        self._checkok('store', typ, data) 
    557  
    558586        return self._flatten_dict(parse_fetch_response((data))) 
    559587 
  • imapclient/imapclient.py

    r204 r243  
    4343class IMAPClient(object): 
    4444    """ 
    45     A Pythonic, easy-to-use IMAP client class. 
    46  
    47     Unlike imaplib, arguments and returns values are Pythonic and readily 
    48     usable. Exceptions are raised when problems occur (no error checking of 
    49     return values is required). 
    50  
    51     Message unique identifiers (UID) can be used with any call. The use_uid 
    52     argument to the constructor and the use_uid attribute control whether UIDs 
    53     are used. 
    54  
    55     Any method that accepts message id's takes either a sequence containing 
    56     message IDs (eg. [1,2,3]) or a single message ID as an integer. 
    57  
    58     Any method that accepts message flags takes either a sequence containing 
    59     message flags (eg. [DELETED, 'foo', 'Bar']) or a single message flag (eg. 
    60     'Foo'). See the constants at the top of this file for commonly used flags. 
    61  
    62     Any method that takes a folder name will accept a standard string or a 
    63     unicode string. Unicode strings will be transparently encoded using 
    64     modified UTF-7 as specified by RFC-2060. Such folder names will be returned 
    65     as unicode strings by methods that return folder names. 
    66  
    67     Transparent folder name encoding can be enabled or disabled with the 
    68     folder_encode attribute. It defaults to True. 
    69  
    70     The IMAP related exceptions that will be raised by this class are: 
    71         IMAPClient.Error 
    72         IMAPClient.AbortError 
    73         IMAPClient.ReadOnlyError 
    74     These are aliases for the imaplib.IMAP4 exceptions of the same name. Socket 
    75     errors may also be raised in the case of network errors. 
     45    A connection to the IMAP server specified by *host* is made when 
     46    the class is instantiated. 
     47 
     48    *port* defaults to 143, or 993 if *ssl* is ``True``. 
     49 
     50    If *use_uid* is ``True`` unique message UIDs be used for all calls 
     51    that accept message ids (defaults to ``True``). 
     52 
     53    If *ssl* is ``True`` an SSL connection will be made (defaults to 
     54    ``False``). 
    7655    """ 
    7756 
     
    8463 
    8564    def __init__(self, host, port=None, use_uid=True, ssl=False): 
    86         """Initialise object instance and connect to the remote IMAP server. 
    87  
    88         @param host: The IMAP server address/hostname to connect to. 
    89         @param port: The port number to use (default is 143, 993 for SSL). 
    90         @param use_uid: Should message UIDs be used (default is True). 
    91         @param ssl: Make an SSL connection (default is False) 
    92         """ 
    9365        if ssl: 
    9466            ImapClass = imaplib.IMAP4_SSL 
     
    10779    
    10880    def login(self, username, password): 
    109         """Perform a simple login 
     81        """Login using *username* and *password*, returning the 
     82        server response. 
    11083        """ 
    11184        typ, data = self._imap.login(username, password) 
     
    11588 
    11689    def logout(self): 
    117         """Perform a logout 
     90        """Logout, returning the server response. 
    11891        """ 
    11992        typ, data = self._imap.logout() 
     
    12396 
    12497    def capabilities(self): 
    125         """Returns the server capability list 
     98        """Returns the server capability list. 
    12699        """ 
    127100        return self._imap.capabilities 
     
    129102 
    130103    def has_capability(self, capability): 
    131         """Checks if the server has the given capability. 
    132  
    133         @param capability: capability to test (eg 'SORT') 
     104        """Return ``True`` if the IMAP server has the given *capability*. 
    134105        """ 
    135106        # FIXME: this will not detect capabilities that are backwards 
     
    176147 
    177148    def list_folders(self, directory="", pattern="*"): 
    178         """Get a listing of folders on the server. 
    179  
    180         The default behaviour (no args) will list all folders for the logged in 
    181         user. 
    182  
    183         @param directory: The base directory to look for folders from. 
    184         @param pattern: A pattern to match against folder names. Only folder 
    185             names matching this pattern will be returned. Wildcards accepted. 
    186         @return: A list of (flags, delim, folder_name). Each folder name will 
    187             be either a string or a unicode string (if the folder on the 
    188             server required decoding). If the folder_encode attribute is 
    189             False, no decoding will be performed and only ordinary strings 
    190             will be returned. 
     149        """Get a listing of folders on the server as a list of 
     150        ``(flags, delimiter, name)`` tuples. 
     151 
     152        Calling list_folders with no arguments will list all 
     153        folders. 
     154 
     155        Specifying *directory* will limit returned folders to that 
     156        base directory. Specifying *pattern* will limit returned 
     157        folders to those with matching names. Wildcards (``*`` and 
     158        ``?``) are supported in *pattern*. 
     159 
     160        #XXX check wildcard facts 
     161 
     162        Each folder name will be either a string or a unicode string 
     163        (if the folder on the server required decoding). If the 
     164        folder_encode attribute is False, no decoding will be 
     165        performed and only ordinary strings will be returned. 
     166 
     167        #XXX check the above is still true 
    191168        """ 
    192169        return self._do_list('LIST', directory, pattern) 
    193170 
    194171    def xlist_folders(self, directory="", pattern="*"): 
    195         """A gmail-specific IMAP extension. 
    196          
    197         This method returns special flags for each folder and a localized name 
    198         for certain folders (eg, the name of the 'inbox' may be localized as the 
    199         flags can be used to determine the actual inbox, even if the name has 
    200         been localized.  It is the responsibility of the caller to either check 
    201         for 'XLIST' in the server capabilites, or to handle the error if the 
    202         server doesn't support this externsion. 
    203  
    204         @param directory: The base directory to look for folders from. 
    205         @param pattern: A pattern to match against folder names. Only folder 
    206             names matching this pattern will be returned. Wildcards accepted. 
    207         @return: A list of (flags, delim, folder_name). As per the return of 
    208             list_folders(). 
     172        """Execute the XLIST command, returning``(flags, delimiter, 
     173        name)`` tuples. 
     174 
     175        This method returns special flags for each folder and a 
     176        localized name for certain folders (e.g. the name of the 
     177        'inbox' may be localized and the flags can be used to 
     178        determine the actual inbox, even if the name has been 
     179        localized. 
     180 
     181        XXX example response 
     182                                             
     183        This is a Gmail-specific IMAP extension. It is the 
     184        responsibility of the caller to either check for 'XLIST' in 
     185        the server capabilites, or to handle the error if the server 
     186        doesn't support this externsion. 
     187 
     188        The *directory* and *pattern* arguments are as per 
     189        list_folders(). 
    209190        """ 
    210191        return self._do_list('XLIST', directory, pattern) 
    211192 
    212193    def list_sub_folders(self, directory="", pattern="*"): 
    213         """Get a listing of subscribed folders on the server. 
    214  
    215         The default behaviour (no args) will list all subscribed folders for the 
    216         logged in user. 
    217  
    218         @param directory: The base directory to look for folders from. 
    219         @param pattern: A pattern to match against folder names. Only folder 
    220             names matching this pattern will be returned. Wildcards accepted. 
    221         @return: A list of (flags, delim, folder_name). As per the return of 
    222             list_folders(). 
     194        """Return a list of subscribed folders on the server as 
     195        ``(flags, delimiter, name)`` tuples. 
     196 
     197        The default behaviour will list all subscribed folders. The 
     198        *directory* and *pattern* arguments are as per list_folders(). 
    223199        """ 
    224200        return self._do_list('LSUB', directory, pattern) 
     
    284260 
    285261    def folder_status(self, folder, what=None): 
    286         """Requests the status from folder. 
    287  
    288         @param folder: The folder name. 
    289         @param what: A sequence of status items to query. Defaults to 
    290             ('MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN'). 
    291         @return: Dictionary of the status items for the folder. The keys match 
    292             the items specified in the what parameter. 
    293         @rtype: dict 
     262        """Return the status of *folder*. 
     263 
     264        *what* should be a sequence of status items to query. This 
     265        defaults to ``('MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 
     266        'UNSEEN')``. 
     267 
     268        Returns a dictionary of the status items for the folder with 
     269        keys matching *what*. 
    294270        """ 
    295271        if what is None: 
     
    316292 
    317293    def close_folder(self): 
    318         """Close the currently selected folder. 
    319  
    320         @return: Server response. 
     294        """Close the currently selected folder, returning the server 
     295        response string. 
    321296        """ 
    322297        typ, data = self._imap.close() 
     
    324299        return data[0] 
    325300 
    326  
    327301    def create_folder(self, folder): 
    328         """Create a new folder on the server. 
    329  
    330         @param folder: The folder name. 
    331         @return: Server response. 
     302        """Create *folder* on the server returning the server response string. 
    332303        """ 
    333304        typ, data = self._imap.create(self._encode_folder_name(folder)) 
     
    335306        return data[0] 
    336307 
    337  
    338308    def delete_folder(self, folder): 
    339         """Delete a new folder on the server. 
    340  
    341         @param folder: Folder name to delete. 
    342         @return: Server response. 
     309        """Delete *folder* on the server returning the server response string. 
    343310        """ 
    344311        typ, data = self._imap.delete(self._encode_folder_name(folder)) 
     
    346313        return data[0] 
    347314 
    348  
    349315    def folder_exists(self, folder): 
    350         """Determine if a folder exists on the server. 
    351  
    352         @param folder: Full folder name to look for. 
    353         @return: True if the folder exists. False otherwise. 
     316        """Return True if *folder* exists on the server. 
    354317        """ 
    355318        typ, data = self._imap.list('', self._encode_folder_name(folder)) 
     
    358321        return len(data) == 1 and data[0] != None 
    359322 
    360  
    361323    def subscribe_folder(self, folder): 
    362         """Subscribe to a folder. 
    363  
    364         @param folder: Folder name to subscribe to. 
    365         @return: Server response message. 
     324        """Subscribe to *folder*, returning the server response string. 
    366325        """ 
    367326        typ, data = self._imap.subscribe(self._encode_folder_name(folder)) 
     
    369328        return data 
    370329 
    371  
    372330    def unsubscribe_folder(self, folder): 
    373         """Unsubscribe a folder. 
    374  
    375         @param folder: Folder name to unsubscribe. 
    376         @return: Server response message. 
     331        """Unsubscribe to *folder*, returning the server response string. 
    377332        """ 
    378333        typ, data = self._imap.unsubscribe(self._encode_folder_name(folder)) 
     
    380335        return data 
    381336 
    382  
    383337    def search(self, criteria='ALL', charset=None): 
     338        """Return a list of messages ids matching *criteria*. 
     339 
     340        XXX more detail 
     341        """ 
    384342        if not criteria: 
    385343            raise ValueError('no criteria specified') 
     
    406364 
    407365    def sort(self, sort_criteria, criteria='ALL', charset='UTF-8' ): 
    408         """Returns a list of messages sorted by sort_criteria. 
     366        """Return a list of message ids sorted by *sort_criteria* and 
     367        optionally filtered by *criteria*. 
     368 
     369        The *critera* are as per search().   
    409370 
    410371        Note that this is an extension to the IMAP4: 
    411372        http://www.ietf.org/internet-drafts/draft-ietf-imapext-sort-19.txt 
     373 
     374        XXX needs more detail 
     375        XXX explain charset 
    412376        """ 
    413377        if not criteria: 
     
    435399 
    436400    def get_flags(self, messages): 
    437         """Return the flags set for messages 
    438  
    439         @param messages: Message IDs to check flags for 
    440         @return: As for add_f 
    441             { msgid1: [flag1, flag2, ... ], } 
     401        """Returns the flags set for each message in *messages* as a 
     402        dictionary structured like this: 
     403          ``{ msgid1: [flag1, flag2, ... ], }``. 
    442404        """ 
    443405        response = self.fetch(messages, ['FLAGS']) 
     
    446408 
    447409    def add_flags(self, messages, flags): 
    448         """Add one or more flags to messages 
    449  
    450         @param messages: Message IDs to add flags to 
    451         @param flags: Sequence of flags to add 
    452         @return: The flags set for each message ID as a dictionary 
    453             { msgid1: [flag1, flag2, ... ], } 
     410        """Add *flags* to *messages*. 
     411 
     412        *flags* should be a sequence of strings. Returns the flags set 
     413         for each modified message (see *get_flags*). 
    454414        """ 
    455415        return self._store('+FLAGS', messages, flags) 
     
    457417 
    458418    def remove_flags(self, messages, flags): 
    459         """Remove one or more flags from messages 
    460  
    461         @param messages: Message IDs to remove flags from 
    462         @param flags: Sequence of flags to remove 
    463         @return: As for get_flags. 
     419        """Remove one or more *flags* from *messages*. 
     420 
     421        *flags* should be a sequence of strings. Returns the flags set 
     422         for each modified message (see *get_flags*). 
    464423        """ 
    465424        return self._store('-FLAGS', messages, flags) 
     
    467426 
    468427    def set_flags(self, messages, flags): 
    469         """Set the flags for messages 
    470  
    471         @param messages: Message IDs to set flags for 
    472         @param flags: Sequence of flags to set 
    473         @return: As for get_flags. 
     428        """Set the *flags* for *messages*. 
     429 
     430        *flags* should be a sequence of strings. Returns the flags set 
     431         for each modified message (see *get_flags*). 
    474432        """ 
    475433        return self._store('FLAGS', messages, flags) 
     
    477435 
    478436    def delete_messages(self, messages): 
    479         """Short-hand method for deleting one or more messages 
    480  
    481         @param messages: Message IDs to mark for deletion. 
    482         @return: Same as for get_flags. 
     437        """Delete one or more *messages* from the currently selected 
     438        folder. 
     439 
     440        Returns the flags set for each modified message (see 
     441        *get_flags*). 
    483442        """ 
    484443        return self.add_flags(messages, DELETED) 
     
    515474        return parse_fetch_response(data) 
    516475 
    517  
    518476    def append(self, folder, msg, flags=(), msg_time=None): 
    519         """Append a message to a folder 
    520  
    521         @param folder: Folder name to append to. 
    522         @param msg: Message body as a string. 
    523         @param flags: Sequnce of message flags to set. If not specified no 
    524             flags will be set. 
    525         @param msg_time: Optional date and time to set for the message. The 
    526             server will set a time if it isn't specified. If msg_time contains 
    527             timezone information (tzinfo), this will be honoured. Otherwise the 
    528             local machine's time zone sent to the server. 
    529         @type msg_time: datetime.datetime 
    530         @return: The append response returned by the server. 
    531         @rtype: str 
     477        """Append a message to *folder*. 
     478 
     479        *msg* should be a string contains the full message including 
     480        headers. 
     481 
     482        *flags* should be a sequence of message flags to set. If not 
     483        specified no flags will be set. 
     484 
     485        *msg_time* is an optional datetime instance specifying the 
     486        date and time to set on the message. The server will set a 
     487        time if it isn't specified. If *msg_time* contains timezone 
     488        information (tzinfo), this will be honoured. Otherwise the 
     489        local machine's time zone sent to the server. 
     490 
     491        Returns the APPEND response as returned by the server. 
    532492        """ 
    533493        if msg_time: 
     
    544504        return data[0] 
    545505 
    546  
    547506    def copy(self, messages, folder): 
    548         """Copy one or more messages from the current folder to another folder 
    549  
    550         @param messages: Message IDs to fetch. 
    551         @param folder: Folder name to append to. 
    552         @return: The COPY command response message returned by the 
    553           server. 
     507        """Copy one or more messages from the current folder to 
     508        *folder*. Returns the COPY response string returned by the 
     509        server. 
    554510        """ 
    555511        msg_list = messages_to_str(messages) 
     
    563519        return data[0] 
    564520 
    565  
    566521    def expunge(self): 
     522        """Remove any messaages from the server that have the \Deleted 
     523        flag set. 
     524 
     525        XXX check if this is just for the current folder 
     526        """ 
    567527        typ, data = self._imap.expunge() 
    568528        self._checkok('expunge', typ, data) 
    569529        #TODO: expunge response 
    570530 
    571  
    572531    def getacl(self, folder): 
    573         """Get the ACL for a folder 
    574  
    575         @param folder: Folder name to get the ACL for. 
    576         @return: A list of (who, acl) tuples 
     532        """Returns a list of ``(who, acl)`` tuples describing the 
     533        access controls for *folder*. 
    577534        """ 
    578535        typ, data = self._imap.getacl(folder) 
     
    587544        return out 
    588545 
    589  
    590546    def setacl(self, folder, who, what): 
    591         """Set an ACL for a folder 
    592  
    593         @param folder: Folder name to set an ACL for. 
    594         @param who: User or group ID for the ACL. 
    595         @param what: A string describing the ACL. Set to '' to remove an ACL. 
    596         @return: Server response string. 
     547        """Set an ACL (*what*) for user (*who*) for a folder. 
     548 
     549        Set *what* to an empty string to remove an ACL. Returns the 
     550        server response string. 
    597551        """ 
    598552        typ, data = self._imap.setacl(folder, who, what) 
     
    600554        return data[0] 
    601555 
    602  
    603556    def _check_resp(self, expected, command, typ, data): 
    604557        """Check command responses for errors. 
    605558 
    606         @raise: Error if a command failed. 
     559        Raises IMAPClient.Error if the command fails. 
    607560        """ 
    608561        if typ != expected: 
    609562            raise self.Error('%s failed: %r' % (command, data[0])) 
    610563 
    611  
    612564    def _checkok(self, command, typ, data): 
    613565        self._check_resp('OK', command, typ, data) 
    614566 
    615  
    616567    def _checkbye(self, command, typ, data): 
    617568        self._check_resp('BYE', command, typ, data) 
    618569 
    619  
    620570    def _store(self, cmd, messages, flags): 
    621         """Worker function for flag manipulation functions 
    622  
    623         @param cmd: STORE command to use (eg. '+FLAGS') 
    624         @param messages: Sequence of message IDs 
    625         @param flags: Sequence of flags to set. 
    626         @return: The flags set for each message ID as a dictionary 
    627             { msgid1: [flag1, flag2, ... ], } 
     571        """Worker function for the various flag manipulation methods. 
     572 
     573        *cmd* is the STORE command to use (eg. '+FLAGS'). 
    628574        """ 
    629575        if not messages: 
     
    639585        self._checkok('store', typ, data) 
    640586        return self._flatten_dict(parse_fetch_response((data))) 
    641  
    642587 
    643588    def _flatten_dict(self, fetch_dict): 
     
    651596            return imap_utf7.decode(name) 
    652597        return name 
    653  
    654598 
    655599    def _encode_folder_name(self, name): 
     
    669613 
    670614def messages_to_str(messages): 
    671     """Convert a sequence of messages ids or a single message id into an 
    672     message ID list for use with IMAP commands. 
    673  
    674     @param messages: A sequence of messages IDs or a single message ID. 
    675         (eg. [1,4,5,7,8]) 
    676     @return: Message list string (eg. "1,4,5,6,8") 
     615    """Convert a sequence of messages ids or a single integer message id 
     616    into an id list string for use with IMAP commands 
    677617    """ 
    678618    if isinstance(messages, (str, int, long)): 
     
    684624 
    685625def seq_to_parenlist(flags): 
    686     """Convert a sequence into parenthised list for use with IMAP commands 
    687  
    688     @param flags: Sequence to process (eg. ['abc', 'def']) 
    689     @return: IMAP parenthenised list (eg. '(abc def)') 
     626    """Convert a sequence of strings into parenthised list string for 
     627    use with IMAP commands. 
    690628    """ 
    691629    if isinstance(flags, str): 
     
    697635 
    698636def datetime_to_imap(dt): 
    699     """Convert a datetime instance to a IMAP datetime string 
    700  
    701     If timezone information is missing the current system timezone is used. 
     637    """Convert a datetime instance to a IMAP datetime string. 
     638 
     639    If timezone information is missing the current system 
     640    timezone is used. 
    702641    """ 
    703642    if not dt.tzinfo: