Changeset 239:86b79856cb3d
- Timestamp:
- 06/11/10 18:29:11 (19 months ago)
- Branch:
- default
- Files:
-
- 2 modified
-
doc-src/index.rst (modified) (1 diff)
-
imapclient/imapclient.py (modified) (31 diffs)
Legend:
- Unmodified
- Added
- Removed
-
doc-src/index.rst
r237 r239 12 12 Introduction 13 13 ------------ 14 XXX 14 A Pythonic, easy-to-use IMAP client class. 15 16 Unlike imaplib, arguments and returns values are Pythonic and readily 17 usable. Exceptions are raised when problems occur (no error checking 18 of return values is required). 15 19 16 20 A Simple Example 17 21 ---------------- 18 22 .. literalinclude:: ../imapclient/examples/example.py 23 24 XXX include some simple output 19 25 20 26 IMAPClient Class Reference -
imapclient/imapclient.py
r156 r239 32 32 33 33 class IMAPClient(object): 34 #XXX concepts to cover: UIDs, message lists, folder encoding, exceptions 34 35 """ 35 A Pythonic, easy-to-use IMAP client class.36 37 Unlike imaplib, arguments and returns values are Pythonic and readily38 usable. Exceptions are raised when problems occur (no error checking of39 return values is required).40 41 36 Message unique identifiers (UID) can be used with any call. The use_uid 42 37 argument to the constructor and the use_uid attribute control whether UIDs … … 98 93 99 94 def login(self, username, password): 100 """Perform a simple login 95 """Login using *username* and *password*, returning the 96 server response. 101 97 """ 102 98 typ, data = self._imap.login(username, password) … … 106 102 107 103 def logout(self): 108 """ Perform a logout104 """Logout, returning the server response. 109 105 """ 110 106 typ, data = self._imap.logout() … … 114 110 115 111 def capabilities(self): 116 """Returns the server capability list 112 """Returns the server capability list. 117 113 """ 118 114 return self._imap.capabilities … … 120 116 121 117 def has_capability(self, capability): 122 """Checks if the server has the given capability. 123 124 @param capability: capability to test (eg 'SORT') 118 """Return True if the server has the *capability* specified. 125 119 """ 126 120 # FIXME: this will not detect capabilities that are backwards … … 135 129 136 130 def get_folder_delimiter(self): 137 """Determine the folder separator used by the IMAP server. 138 139 @return: The folder separator. 140 @rtype: string 131 """Return the folder separator used by the IMAP server (eg. "/"). 141 132 """ 142 133 typ, data = self._imap.namespace() … … 150 141 151 142 def list_folders(self, directory="", pattern="*"): 152 """Get a listing of folders on the server. 153 154 The default behaviour (no args) will list all folders for the logged in 155 user. 156 157 @param directory: The base directory to look for folders from. 158 @param pattern: A pattern to match against folder names. Only folder 159 names matching this pattern will be returned. Wildcards accepted. 160 @return: A list of (flags, delim, folder_name). Each folder name will 161 be either a string or a unicode string (if the folder on the 162 server required decoding). If the folder_encode attribute is 163 False, no decoding will be performed and only ordinary strings 164 will be returned. 143 """Get a listing of folders on the server as a list of 144 ``(flags, delimiter, name)`` tuples. 145 146 Calling list_folders with no arguments will list all 147 folders. 148 149 Specifying *directory* will limit returned folders to that 150 base directory. Specifying *pattern* will limit returned 151 folders to those with matching names. Wildcards (``*`` and 152 ``?``) are supported in *pattern*. 153 154 #XXX check wildcard facts 155 156 Each folder name will be either a string or a unicode string 157 (if the folder on the server required decoding). If the 158 folder_encode attribute is False, no decoding will be 159 performed and only ordinary strings will be returned. 160 161 #XXX check the above is still true 165 162 """ 166 163 return self._do_list('LIST', directory, pattern) 167 164 168 165 def xlist_folders(self, directory="", pattern="*"): 169 """A gmail-specific IMAP extension. 170 171 This method returns special flags for each folder and a localized name 172 for certain folders (eg, the name of the 'inbox' may be localized as the 173 flags can be used to determine the actual inbox, even if the name has 174 been localized. It is the responsibility of the caller to either check 175 for 'XLIST' in the server capabilites, or to handle the error if the 176 server doesn't support this externsion. 177 178 @param directory: The base directory to look for folders from. 179 @param pattern: A pattern to match against folder names. Only folder 180 names matching this pattern will be returned. Wildcards accepted. 181 @return: A list of (flags, delim, folder_name). As per the return of 182 list_folders(). 166 """Execute the XLIST command, returning``(flags, delimiter, 167 name)`` tuples. 168 169 This method returns special flags for each folder and a 170 localized name for certain folders (e.g. the name of the 171 'inbox' may be localized and the flags can be used to 172 determine the actual inbox, even if the name has been 173 localized. 174 175 XXX example response 176 177 This is a Gmail-specific IMAP extension. It is the 178 responsibility of the caller to either check for 'XLIST' in 179 the server capabilites, or to handle the error if the server 180 doesn't support this externsion. 181 182 The *directory* and *pattern* arguments are as per 183 list_folders(). 183 184 """ 184 185 return self._do_list('XLIST', directory, pattern) 185 186 186 187 def list_sub_folders(self, directory="", pattern="*"): 187 """Get a listing of subscribed folders on the server. 188 189 The default behaviour (no args) will list all subscribed folders for the 190 logged in user. 191 192 @param directory: The base directory to look for folders from. 193 @param pattern: A pattern to match against folder names. Only folder 194 names matching this pattern will be returned. Wildcards accepted. 195 @return: A list of (flags, delim, folder_name). As per the return of 196 list_folders(). 188 """Return a list of subscribed folders on the server as 189 ``(flags, delimiter, name)`` tuples. 190 191 The default behaviour will list all subscribed folders. The 192 *directory* and *pattern* arguments are as per list_folders(). 197 193 """ 198 194 return self._do_list('LSUB', directory, pattern) … … 258 254 259 255 def folder_status(self, folder, what=None): 260 """Re quests the status from folder.261 262 @param folder: The folder name.263 @param what: A sequence of status items to query. Defaults to264 ('MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN').265 @return: Dictionary of the status items for the folder. The keys match 266 the items specified in the what parameter.267 @rtype: dict256 """Return the status of *folder*. 257 258 *what* should be a sequence of status items to query. This 259 defaults to ``('MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 260 'UNSEEN')``. 261 262 Returns a dictionary of the status items for the folder with 263 keys matching *what*. 268 264 """ 269 265 if what is None: … … 290 286 291 287 def close_folder(self): 292 """Close the currently selected folder. 293 294 @return: Server response. 288 """Close the currently selected folder, returning the server 289 response string. 295 290 """ 296 291 typ, data = self._imap.close() … … 298 293 return data[0] 299 294 300 301 295 def create_folder(self, folder): 302 """Create a new folder on the server. 303 304 @param folder: The folder name. 305 @return: Server response. 296 """Create *folder* on the server returning the server response string. 306 297 """ 307 298 typ, data = self._imap.create(self._encode_folder_name(folder)) … … 309 300 return data[0] 310 301 311 312 302 def delete_folder(self, folder): 313 """Delete a new folder on the server. 314 315 @param folder: Folder name to delete. 316 @return: Server response. 303 """Delete *folder* on the server returning the server response string. 317 304 """ 318 305 typ, data = self._imap.delete(self._encode_folder_name(folder)) … … 320 307 return data[0] 321 308 322 323 309 def folder_exists(self, folder): 324 """Determine if a folder exists on the server. 325 326 @param folder: Full folder name to look for. 327 @return: True if the folder exists. False otherwise. 310 """Return True if *folder* exists on the server. 328 311 """ 329 312 typ, data = self._imap.list('', self._encode_folder_name(folder)) … … 332 315 return len(data) == 1 and data[0] != None 333 316 334 335 317 def subscribe_folder(self, folder): 336 """Subscribe to a folder. 337 338 @param folder: Folder name to subscribe to. 339 @return: Server response message. 318 """Subscribe to *folder*, returning the server response string. 340 319 """ 341 320 typ, data = self._imap.subscribe(self._encode_folder_name(folder)) … … 343 322 return data 344 323 345 346 324 def unsubscribe_folder(self, folder): 347 """Unsubscribe a folder. 348 349 @param folder: Folder name to unsubscribe. 350 @return: Server response message. 325 """Unsubscribe to *folder*, returning the server response string. 351 326 """ 352 327 typ, data = self._imap.unsubscribe(self._encode_folder_name(folder)) … … 354 329 return data 355 330 356 357 331 def search(self, criteria='ALL', charset=None): 332 """Return a list of messages ids matching *criteria*. 333 334 XXX more detail 335 """ 358 336 if not criteria: 359 337 raise ValueError('no criteria specified') … … 380 358 381 359 def sort(self, sort_criteria, criteria='ALL', charset='UTF-8' ): 382 """Returns a list of messages sorted by sort_criteria. 360 """Return a list of message ids sorted by *sort_criteria* and 361 optionally filtered by *criteria*. 362 363 The *critera* are as per search(). 383 364 384 365 Note that this is an extension to the IMAP4: 385 366 http://www.ietf.org/internet-drafts/draft-ietf-imapext-sort-19.txt 367 368 XXX needs more detail 369 XXX explain charset 386 370 """ 387 371 if not criteria: … … 409 393 410 394 def get_flags(self, messages): 411 """Return the flags set for messages 412 413 @param messages: Message IDs to check flags for 414 @return: As for add_f 415 { msgid1: [flag1, flag2, ... ], } 395 """Returns the flags set for each message in *messages* as a 396 dictionary structured like this: 397 ``{ msgid1: [flag1, flag2, ... ], }``. 416 398 """ 417 399 response = self.fetch(messages, ['FLAGS']) … … 420 402 421 403 def add_flags(self, messages, flags): 422 """Add one or more flags to messages 423 424 @param messages: Message IDs to add flags to 425 @param flags: Sequence of flags to add 426 @return: The flags set for each message ID as a dictionary 427 { msgid1: [flag1, flag2, ... ], } 404 """Add *flags* to *messages*. 405 406 *flags* should be a sequence of strings. Returns the flags set 407 for each modified message (see *get_flags*). 428 408 """ 429 409 return self._store('+FLAGS', messages, flags) … … 431 411 432 412 def remove_flags(self, messages, flags): 433 """Remove one or more flags from messages 434 435 @param messages: Message IDs to remove flags from 436 @param flags: Sequence of flags to remove 437 @return: As for get_flags. 413 """Remove one or more *flags* from *messages*. 414 415 *flags* should be a sequence of strings. Returns the flags set 416 for each modified message (see *get_flags*). 438 417 """ 439 418 return self._store('-FLAGS', messages, flags) … … 441 420 442 421 def set_flags(self, messages, flags): 443 """Set the flags for messages 444 445 @param messages: Message IDs to set flags for 446 @param flags: Sequence of flags to set 447 @return: As for get_flags. 422 """Set the *flags* for *messages*. 423 424 *flags* should be a sequence of strings. Returns the flags set 425 for each modified message (see *get_flags*). 448 426 """ 449 427 return self._store('FLAGS', messages, flags) … … 451 429 452 430 def delete_messages(self, messages): 453 """Short-hand method for deleting one or more messages 454 455 @param messages: Message IDs to mark for deletion. 456 @return: Same as for get_flags. 431 """Delete one or more *messages* from the currently selected 432 folder. 433 434 Returns the flags set for each modified message (see 435 *get_flags*). 457 436 """ 458 437 return self.add_flags(messages, DELETED) … … 489 468 return parse_fetch_response(data) 490 469 491 492 470 def append(self, folder, msg, flags=(), msg_time=None): 493 """Append a message to a folder 494 495 @param folder: Folder name to append to. 496 @param msg: Message body as a string. 497 @param flags: Sequnce of message flags to set. If not specified no 498 flags will be set. 499 @param msg_time: Optional date and time to set for the message. The 500 server will set a time if it isn't specified. If msg_time contains 501 timezone information (tzinfo), this will be honoured. Otherwise the 502 local machine's time zone sent to the server. 503 @type msg_time: datetime.datetime 504 @return: The append response returned by the server. 505 @rtype: str 471 """Append a message to *folder*. 472 473 *msg* should be a string contains the full message including 474 headers. 475 476 *flags* should be a sequence of message flags to set. If not 477 specified no flags will be set. 478 479 *msg_time* is an optional datetime instance specifying the 480 date and time to set on the message. The server will set a 481 time if it isn't specified. If *msg_time* contains timezone 482 information (tzinfo), this will be honoured. Otherwise the 483 local machine's time zone sent to the server. 484 485 Returns the APPEND response as returned by the server. 506 486 """ 507 487 if msg_time: … … 518 498 return data[0] 519 499 520 521 500 def copy(self, messages, folder): 522 """Copy one or more messages from the current folder to another folder 523 524 @param messages: Message IDs to fetch. 525 @param folder: Folder name to append to. 526 @return: The COPY command response message returned by the 527 server. 501 """Copy one or more messages from the current folder to 502 *folder*. Returns the COPY response string returned by the 503 server. 528 504 """ 529 505 msg_list = messages_to_str(messages) … … 537 513 return data[0] 538 514 539 540 515 def expunge(self): 516 """Remove any messaages from the server that have the \Deleted 517 flag set. 518 519 XXX check if this is just for the current folder 520 """ 541 521 typ, data = self._imap.expunge() 542 522 self._checkok('expunge', typ, data) 543 523 #TODO: expunge response 544 524 545 546 525 def getacl(self, folder): 547 """Get the ACL for a folder 548 549 @param folder: Folder name to get the ACL for. 550 @return: A list of (who, acl) tuples 526 """Returns a list of ``(who, acl)`` tuples describing the 527 access controls for *folder*. 551 528 """ 552 529 typ, data = self._imap.getacl(folder) … … 561 538 return out 562 539 563 564 540 def setacl(self, folder, who, what): 565 """Set an ACL for a folder 566 567 @param folder: Folder name to set an ACL for. 568 @param who: User or group ID for the ACL. 569 @param what: A string describing the ACL. Set to '' to remove an ACL. 570 @return: Server response string. 541 """Set an ACL (*what*) for user (*who*) for a folder. 542 543 Set *what* to an empty string to remove an ACL. Returns the 544 server response string. 571 545 """ 572 546 typ, data = self._imap.setacl(folder, who, what) … … 574 548 return data[0] 575 549 576 577 550 def _check_resp(self, expected, command, typ, data): 578 551 """Check command responses for errors. 579 552 580 @raise: Error if a command failed.553 Raises IMAPClient.Error if the command fails. 581 554 """ 582 555 if typ != expected: 583 556 raise self.Error('%s failed: %r' % (command, data[0])) 584 557 585 586 558 def _checkok(self, command, typ, data): 587 559 self._check_resp('OK', command, typ, data) 588 560 589 590 561 def _checkbye(self, command, typ, data): 591 562 self._check_resp('BYE', command, typ, data) 592 563 593 594 564 def _store(self, cmd, messages, flags): 595 """Worker function for flag manipulation functions 596 597 @param cmd: STORE command to use (eg. '+FLAGS') 598 @param messages: Sequence of message IDs 599 @param flags: Sequence of flags to set. 600 @return: The flags set for each message ID as a dictionary 601 { msgid1: [flag1, flag2, ... ], } 565 """Worker function for the various flag manipulation methods. 566 567 *cmd* is the STORE command to use (eg. '+FLAGS'). 602 568 """ 603 569 if not messages: … … 614 580 615 581 return self._flatten_dict(parse_fetch_response((data))) 616 617 582 618 583 def _flatten_dict(self, fetch_dict): … … 626 591 return imap_utf7.decode(name) 627 592 return name 628 629 593 630 594 def _encode_folder_name(self, name): … … 644 608 645 609 def messages_to_str(messages): 646 """Convert a sequence of messages ids or a single message id into an 647 message ID list for use with IMAP commands. 648 649 @param messages: A sequence of messages IDs or a single message ID. 650 (eg. [1,4,5,7,8]) 651 @return: Message list string (eg. "1,4,5,6,8") 610 """Convert a sequence of messages ids or a single integer message id 611 into an id list string for use with IMAP commands 652 612 """ 653 613 if isinstance(messages, (str, int, long)): … … 659 619 660 620 def seq_to_parenlist(flags): 661 """Convert a sequence into parenthised list for use with IMAP commands 662 663 @param flags: Sequence to process (eg. ['abc', 'def']) 664 @return: IMAP parenthenised list (eg. '(abc def)') 621 """Convert a sequence of strings into parenthised list string for 622 use with IMAP commands. 665 623 """ 666 624 if isinstance(flags, str): … … 672 630 673 631 def datetime_to_imap(dt): 674 """Convert a datetime instance to a IMAP datetime string 675 676 If timezone information is missing the current system timezone is used. 632 """Convert a datetime instance to a IMAP datetime string. 633 634 If timezone information is missing the current system 635 timezone is used. 677 636 """ 678 637 if not dt.tzinfo:
