Skip to content

Commit

Permalink
MailBoxFolderManager.status empty, utils.clean_uids ranges, query.Uid…
Browse files Browse the repository at this point in the history
…Range end empty
  • Loading branch information
ikvk committed Aug 18, 2021
1 parent 5d9705d commit a3754fc
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 20 deletions.
14 changes: 7 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,17 @@ BaseMailBox.fetch - first searches email nums by criteria in current folder, the
* *headers_only* = False, get only email headers (without text, html, attachments)
* *bulk* = False, False - fetch each message separately per N commands - low memory consumption, slow; True - fetch all messages per 1 command - high memory consumption, fast

BaseMailBox.uids - search mailbox for matching message uids in current folder
BaseMailBox.uids - search mailbox for matching message uids in current folder, returns [str or None]

* *criteria* = 'ALL', message search criteria (see examples at ./doc/imap_search_criteria.txt)
* *charset* = 'US-ASCII', IANA charset, indicates charset of the strings that appear in the search criteria. See rfc2978
* *miss_no_uid* = True, add None values to result when uid item not matched to pattern
* *criteria* = 'ALL', message search criteria, `query builder <#search-criteria>`_
* *charset* = 'US-ASCII', indicates charset of the strings that appear in the search criteria. See rfc2978
* *miss_no_uid* = True, not add None values to result when uid item not matched to pattern

BaseMailBox.<action> - `copy, move, delete, flag, append <#actions-with-emails>`_

BaseMailBox.folder - `folder manager <#actions-with-folders>`_

BaseMailBox.numbers - search mailbox for matching message numbers in current folder
BaseMailBox.numbers - search mailbox for matching message numbers in current folder, returns [str]

BaseMailBox.box - imaplib.IMAP4/IMAP4_SSL client instance.

Expand Down Expand Up @@ -124,7 +124,7 @@ MailMessage and MailAttachment public attributes are cached by functools.lru_cac
Search criteria
^^^^^^^^^^^^^^^

This chapter about "criteria" and "charset" arguments of MailBox.fetch.
This chapter about "criteria" and "charset" arguments of MailBox methods: fetch, uids, numbers

You can use 3 approaches to build search criteria:

Expand All @@ -141,7 +141,7 @@ You can pass the criteria as bytes in the desired encoding - in this case, the e

.. code-block:: python
mailbox.fetch(A(subject='жёлтый'), charset='utf8')
mailbox.uids(A(subject='жёлтый'), charset='utf8')
Query builder implements all search logic described in `rfc3501 <https://tools.ietf.org/html/rfc3501#section-6.4.4>`_.
It uses this classes:
Expand Down
6 changes: 6 additions & 0 deletions docs/release_notes.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
0.46.0
======
* MailBoxFolderManager.status folder argument now may by equal to None - status of current folder
* utils.clean_uids now accept uid strings with uid sequence ranges, example: *:4,5:7,10
* query.UidRange end argument now may be None, equal to None by default
0.45.0
======
* Renamed BaseMailBox.search -> BaseMailBox.numbers, search are deprecated now
Expand Down
7 changes: 4 additions & 3 deletions imap_tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Lib author: Vladimir Kaukin <[email protected]>
# Project home page: https://github.com/ikvk/imap_tools

from .query import AND, OR, NOT, Header, UidRange, A, O, N, H, U
from .mailbox import BaseMailBox, MailBox, MailBoxUnencrypted
from .message import MailMessage, MailAttachment
from .folder import MailBoxFolderManager
from .consts import MailMessageFlags, MailBoxFolderStatusOptions
from .errors import *

__author__ = 'Vladimir Kaukin <[email protected]>'

__version__ = '0.45.0'
__version__ = '0.46.0'
6 changes: 4 additions & 2 deletions imap_tools/folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,16 @@ def delete(self, folder: str or bytes):
check_command_status(result, MailboxFolderDeleteError)
return result

def status(self, folder: str or bytes, options: [str] or None = None) -> dict:
def status(self, folder: str or bytes or None = None, options: [str] or None = None) -> dict:
"""
Get the status of a folder
:param folder: mailbox folder
:param folder: mailbox folder, current folder if None
:param options: [str] with values from MailBoxFolderStatusOptions.all | None - for get all options
:return: dict with available options keys
"""
command = 'STATUS'
if folder is None:
folder = self.get()
if not options:
options = tuple(MailBoxFolderStatusOptions.all)
for opt in options:
Expand Down
2 changes: 1 addition & 1 deletion imap_tools/mailbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def uids(self, criteria: str or bytes = 'ALL', charset: str = 'US-ASCII', miss_n
Search mailbox for matching message uids in current folder
:param criteria: message search criteria (see examples at ./doc/imap_search_criteria.txt)
:param charset: IANA charset, indicates charset of the strings that appear in the search criteria. See rfc2978
:param miss_no_uid: add None values to result when uid item not matched to pattern
:param miss_no_uid: not add None values to result when uid item not matched to pattern
:return: email message uids
"""
nums = self.numbers(criteria, charset)
Expand Down
15 changes: 10 additions & 5 deletions imap_tools/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,22 +76,27 @@ def __str__(self):

class UidRange:
"""
* - represents the largest number in use.
x:y - represents sequence range, example: 4:*
NOTE: UID range of <value>:* always includes the UID of the last message in the mailbox,
even if <value> is higher than any assigned UID value ->
any UID range with * indicates at least one message (with the highest numbered UID), unless the mailbox is empty.
"""
__slots__ = ('start', 'end')

def __init__(self, start: str, end: str):
def __init__(self, start: str, end: str or None = None):
self.start = str(start).strip()
if not (self.start.isdigit() or self.start == '*'):
raise TypeError('UidRange start arg must be str with digits or *')
self.end = str(end).strip()
if not (self.end.isdigit() or self.end == '*'):
raise TypeError('UidRange end arg must be str with digits or *')
if end is None:
self.end = None
else:
self.end = str(end).strip()
if not (self.end.isdigit() or self.end == '*'):
raise TypeError('UidRange end arg must be str with digits or *')

def __str__(self):
return '{0.start}:{0.end}'.format(self)
return '{}{}'.format(self.start, ':{}'.format(self.end) if self.end else '')


U = UidRange # Short alias
Expand Down
5 changes: 3 additions & 2 deletions imap_tools/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
def clean_uids(uid_set: str or [str] or iter) -> str:
"""
Prepare set of uid for use in IMAP commands
uid RE patterns are not strict and allow invalid combinations, but simple. Example: 2,4:7,9,12:*
:param uid_set:
str, that is comma separated uids
Iterable, that contains str uids
Expand All @@ -20,7 +21,7 @@ def clean_uids(uid_set: str or [str] or iter) -> str:
"""
# str
if type(uid_set) is str:
if re.search(r'^(\d+,)*\d+$', uid_set): # *optimization for already good str
if re.search(r'^([\d*:]+,)*[\d*:]+$', uid_set): # *optimization for already good str
return uid_set
uid_set = uid_set.split(',')
# Generator
Expand All @@ -35,7 +36,7 @@ def clean_uids(uid_set: str or [str] or iter) -> str:
for uid in uid_set_iter:
if type(uid) is not str:
raise TypeError('uid "{}" is not string'.format(str(uid)))
if not uid.strip().isdigit():
if not re.match(r'^[\d*:]+$', uid.strip()):
raise TypeError('Wrong uid: "{}"'.format(uid))
return ','.join(i.strip() for i in uid_set)

Expand Down
6 changes: 6 additions & 0 deletions tests/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,15 @@ def test_converters(self):
self.assertEqual(A(header=H('X-Google-Smtp-Source', '123')), '(HEADER "X-Google-Smtp-Source" "123")')
self.assertEqual(A(uid='1,2'), '(UID 1,2)')
self.assertEqual(A(uid=['3', '4']), '(UID 3,4)')
self.assertEqual(A(uid=['3', '4:*']), '(UID 3,4:*)')
self.assertEqual(A(uid=['3', '*:5']), '(UID 3,*:5)')
self.assertEqual(A(uid=['*']), '(UID *)')
self.assertEqual(A(uid=U('*', '1000')), '(UID *:1000)')
self.assertEqual(A(uid=U('2', '*')), '(UID 2:*)')
self.assertEqual(A(uid=U('*', '*')), '(UID *:*)')
self.assertEqual(A(uid=U('*')), '(UID *)')
self.assertEqual(A(uid=U('*', '12')), '(UID *:12)')
self.assertEqual(A(uid=U('345')), '(UID 345)')

self.assertEqual(A(gmail_label="TestLabel"), '(X-GM-LABELS "TestLabel")')

Expand Down

0 comments on commit a3754fc

Please sign in to comment.