-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
removed Q alias for AND, added new aliases: A for AND, O for OR, N fo…
…r NOT
- Loading branch information
Showing
7 changed files
with
88 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,7 +33,7 @@ Basic | |
^^^^^ | ||
.. code-block:: python | ||
from imap_tools import MailBox, Q | ||
from imap_tools import MailBox, AND | ||
# get list of email subjects from INBOX folder | ||
with MailBox('imap.mail.com').login('[email protected]', 'password') as mailbox: | ||
|
@@ -42,7 +42,7 @@ Basic | |
# get list of email subjects from INBOX folder - equivalent verbose version | ||
mailbox = MailBox('imap.mail.com') | ||
mailbox.login('[email protected]', 'password', initial_folder='INBOX') # or mailbox.folder.set instead 3d arg | ||
subjects = [msg.subject for msg in mailbox.fetch(Q(all=True))] | ||
subjects = [msg.subject for msg in mailbox.fetch(AND(all=True))] | ||
mailbox.logout() | ||
MailBox/MailBoxUnencrypted for create mailbox instance. | ||
|
@@ -101,38 +101,42 @@ Possible search approaches: | |
|
||
.. code-block:: python | ||
from imap_tools import Q, AND, OR, NOT | ||
from imap_tools import AND, OR, NOT | ||
mailbox.fetch(Q(subject='weather')) # query, the str-like object - see below | ||
mailbox.fetch(AND(subject='weather')) # query, the str-like object - see below | ||
mailbox.fetch('TEXT "hello"') # str, use charset arg for non US-ASCII chars | ||
mailbox.fetch(b'TEXT "\xd1\x8f"') # bytes, charset arg is ignored | ||
Implemented query builder for search logic described in `rfc3501 <https://tools.ietf.org/html/rfc3501#section-6.4.4>`_. | ||
See `query examples <https://github.com/ikvk/imap_tools/blob/master/examples/search.py>`_. | ||
|
||
* Class AND and its alias Q are used to combine keys by the logical "and" condition. | ||
* Class OR is used to combine keys by the logical "or" condition. | ||
* Class NOT is used to invert the result of a logical expression. | ||
* Class H (Header) is used to search by headers. | ||
====== ===== ========================================== ============================================================ | ||
Class Alias Usage Arguments | ||
====== ===== ========================================== ============================================================ | ||
AND A combines keys by logical "AND" condition Search keys (see below) | str | ||
OR O combines keys by logical "OR" condition Search keys (see below) | str | ||
NOT N invert the result of a logical expression AND/OR instances | str | ||
Header H for search by headers name: str, value: str | ||
====== ===== ========================================== ============================================================ | ||
|
||
If the "charset" argument is specified in MailBox.fetch, the search string will be encoded to this encoding. | ||
You can change this behavior by overriding MailBox._criteria_encoder or pass criteria as bytes in desired encoding. | ||
|
||
.. code-block:: python | ||
from imap_tools import Q, AND, OR, NOT | ||
from imap_tools import A, AND, OR, NOT | ||
# AND | ||
Q(text='hello', new=True) # '(TEXT "hello" NEW)' | ||
A(text='hello', new=True) # '(TEXT "hello" NEW)' | ||
# OR | ||
OR(text='hello', date=datetime.date(2000, 3, 15)) # '(OR TEXT "hello" ON 15-Mar-2000)' | ||
# NOT | ||
NOT(text='hello', new=True) # 'NOT (TEXT "hello" NEW)' | ||
# complex | ||
Q(OR(from_='[email protected]', text='"the text"'), NOT(OR(Q(answered=False), Q(new=True))), to='[email protected]') | ||
A(OR(from_='[email protected]', text='"the text"'), NOT(OR(A(answered=False), A(new=True))), to='[email protected]') | ||
# encoding | ||
mailbox.fetch(Q(subject='привет'), charset='utf8') # 'привет' will be encoded by MailBox._criteria_encoder | ||
# python note: you can't do: Q(text='two', NOT(subject='one')) | ||
Q(NOT(subject='one'), text='two') # use kwargs after logic classes | ||
mailbox.fetch(A(subject='привет'), charset='utf8') # 'привет' will be encoded by MailBox._criteria_encoder | ||
# python note: you can't do: A(text='two', NOT(subject='one')) | ||
A(NOT(subject='one'), text='two') # use kwargs after logic classes (args) | ||
The search key types are marked with `*` can accepts a sequence of values like list, tuple, set or generator. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,7 +20,7 @@ | |
""" | ||
|
||
import datetime as dt | ||
from imap_tools import AND, OR, NOT, Q, H | ||
from imap_tools import AND, OR, NOT, A, H | ||
|
||
# date in the date list (date=date1 OR date=date3 OR date=date2) | ||
q1 = OR(date=[dt.date(2019, 10, 1), dt.date(2019, 10, 10), dt.date(2019, 10, 15)]) | ||
|
@@ -31,7 +31,7 @@ | |
# "NOT ((OR OR ON 1-Oct-2019 ON 10-Oct-2019 ON 15-Oct-2019))" | ||
|
||
# subject contains "hello" AND date greater than or equal dt.date(2019, 10, 10) | ||
q3 = Q(subject='hello', date_gte=dt.date(2019, 10, 10)) | ||
q3 = A(subject='hello', date_gte=dt.date(2019, 10, 10)) | ||
# "(SUBJECT "hello" SINCE 10-Oct-2019)" | ||
|
||
# from contains one of the address parts | ||
|
@@ -51,9 +51,9 @@ | |
# "(OR (OR TEXT "tag15" SUBJECT "tag15") (OR TEXT "tag10" SUBJECT "tag10"))" | ||
|
||
# header IsSpam contains '++' AND header CheckAntivirus contains '-' | ||
q8 = Q(header=[H('IsSpam', '++'), H('CheckAntivirus', '-')]) | ||
q8 = A(header=[H('IsSpam', '++'), H('CheckAntivirus', '-')]) | ||
# "(HEADER "IsSpam" "++" HEADER "CheckAntivirus" "-")" | ||
|
||
# complex from README | ||
q9 = Q(OR(from_='[email protected]', text='"the text"'), NOT(OR(Q(answered=False), Q(new=True))), to='[email protected]') | ||
q9 = A(OR(from_='[email protected]', text='"the text"'), NOT(OR(A(answered=False), A(new=True))), to='[email protected]') | ||
# "((OR FROM "[email protected]" TEXT "\\"the text\\"") NOT ((OR (UNANSWERED) (NEW))) TO "[email protected]")" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
from .query import Q, AND, OR, NOT, H | ||
from .query import AND, OR, NOT, Header, A, O, N, H | ||
This comment has been minimized.
Sorry, something went wrong. |
||
from .mailbox import * | ||
from .message import * | ||
from .folder import * | ||
from .utils import * | ||
|
||
__version__ = '0.16.1' | ||
__version__ = '0.17.0' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ def get_version(package: str) -> str: | |
author='v.kaukin', | ||
author_email='[email protected]', | ||
description='Working with email and mailbox using IMAP protocol.', | ||
keywords=['imap', 'imap-client', 'python3', 'email'], | ||
keywords=['imap', 'imap-client', 'python3', 'python', 'email'], | ||
classifiers=[ | ||
"Programming Language :: Python :: 3", | ||
"License :: OSI Approved :: Apache Software License", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import unittest | ||
import datetime as dt | ||
|
||
from imap_tools.query import ParamConverter, Q, AND, OR, NOT, H | ||
from imap_tools.query import ParamConverter, A, AND, OR, NOT, H | ||
|
||
|
||
class QueryTest(unittest.TestCase): | ||
|
@@ -33,49 +33,49 @@ def not_fetch(): | |
cleaned_fn('key_does_not_matter', bad) | ||
|
||
def test_converters(self): | ||
self.assertEqual(Q(answered=True), '(ANSWERED)') | ||
self.assertEqual(Q(answered=False), '(UNANSWERED)') | ||
self.assertEqual(Q(seen=True), '(SEEN)') | ||
self.assertEqual(Q(seen=False), '(UNSEEN)') | ||
self.assertEqual(Q(flagged=True), '(FLAGGED)') | ||
self.assertEqual(Q(flagged=False), '(UNFLAGGED)') | ||
self.assertEqual(Q(draft=True), '(DRAFT)') | ||
self.assertEqual(Q(draft=False), '(UNDRAFT)') | ||
self.assertEqual(Q(deleted=True), '(DELETED)') | ||
self.assertEqual(Q(deleted=False), '(UNDELETED)') | ||
self.assertEqual(Q(keyword='KEY1'), '(KEYWORD KEY1)') | ||
self.assertEqual(Q(no_keyword='KEY2'), '(UNKEYWORD KEY2)') | ||
|
||
self.assertEqual(Q(from_='[email protected]'), '(FROM "[email protected]")') | ||
self.assertEqual(Q(to='[email protected]'), '(TO "[email protected]")') | ||
self.assertEqual(Q(subject='hello'), '(SUBJECT "hello")') | ||
self.assertEqual(Q(body='body text'), '(BODY "body text")') | ||
self.assertEqual(Q(body='hi'), '(BODY "hi")') | ||
self.assertEqual(Q(text='"quoted text"'), '(TEXT "\\"quoted text\\"")') | ||
self.assertEqual(Q(text='hi'), '(TEXT "hi")') | ||
self.assertEqual(Q(bcc='[email protected]'), '(BCC "[email protected]")') | ||
self.assertEqual(Q(cc='[email protected]'), '(CC "[email protected]")') | ||
|
||
self.assertEqual(Q(date=dt.date(2000, 3, 15)), '(ON 15-Mar-2000)') | ||
self.assertEqual(Q(date_gte=dt.date(2000, 3, 15)), '(SINCE 15-Mar-2000)') | ||
self.assertEqual(Q(date_lt=dt.date(2000, 3, 15)), '(BEFORE 15-Mar-2000)') | ||
self.assertEqual(Q(sent_date=dt.date(2000, 3, 15)), '(SENTON 15-Mar-2000)') | ||
self.assertEqual(Q(sent_date_gte=dt.date(2000, 3, 15)), '(SENTSINCE 15-Mar-2000)') | ||
self.assertEqual(Q(sent_date_lt=dt.date(2000, 3, 15)), '(SENTBEFORE 15-Mar-2000)') | ||
|
||
self.assertEqual(Q(size_gt=1024), '(LARGER 1024)') | ||
self.assertEqual(Q(size_lt=512), '(SMALLER 512)') | ||
|
||
self.assertEqual(Q(new=True), '(NEW)') | ||
self.assertEqual(Q(old=True), '(OLD)') | ||
self.assertEqual(Q(recent=True), '(RECENT)') | ||
self.assertEqual(Q(all=True), '(ALL)') | ||
|
||
self.assertEqual(Q(header=H('X-Google-Smtp-Source', '123')), '(HEADER "X-Google-Smtp-Source" "123")') | ||
self.assertEqual(Q(uid='1,2'), '(UID 1,2)') | ||
self.assertEqual(Q(uid=['3', '4']), '(UID 3,4)') | ||
|
||
self.assertEqual(Q(gmail_label="TestLabel"), '(X-GM-LABELS "TestLabel")') | ||
self.assertEqual(A(answered=True), '(ANSWERED)') | ||
self.assertEqual(A(answered=False), '(UNANSWERED)') | ||
self.assertEqual(A(seen=True), '(SEEN)') | ||
self.assertEqual(A(seen=False), '(UNSEEN)') | ||
self.assertEqual(A(flagged=True), '(FLAGGED)') | ||
self.assertEqual(A(flagged=False), '(UNFLAGGED)') | ||
self.assertEqual(A(draft=True), '(DRAFT)') | ||
self.assertEqual(A(draft=False), '(UNDRAFT)') | ||
self.assertEqual(A(deleted=True), '(DELETED)') | ||
self.assertEqual(A(deleted=False), '(UNDELETED)') | ||
self.assertEqual(A(keyword='KEY1'), '(KEYWORD KEY1)') | ||
self.assertEqual(A(no_keyword='KEY2'), '(UNKEYWORD KEY2)') | ||
|
||
self.assertEqual(A(from_='[email protected]'), '(FROM "[email protected]")') | ||
self.assertEqual(A(to='[email protected]'), '(TO "[email protected]")') | ||
self.assertEqual(A(subject='hello'), '(SUBJECT "hello")') | ||
self.assertEqual(A(body='body text'), '(BODY "body text")') | ||
self.assertEqual(A(body='hi'), '(BODY "hi")') | ||
self.assertEqual(A(text='"quoted text"'), '(TEXT "\\"quoted text\\"")') | ||
self.assertEqual(A(text='hi'), '(TEXT "hi")') | ||
self.assertEqual(A(bcc='[email protected]'), '(BCC "[email protected]")') | ||
self.assertEqual(A(cc='[email protected]'), '(CC "[email protected]")') | ||
|
||
self.assertEqual(A(date=dt.date(2000, 3, 15)), '(ON 15-Mar-2000)') | ||
self.assertEqual(A(date_gte=dt.date(2000, 3, 15)), '(SINCE 15-Mar-2000)') | ||
self.assertEqual(A(date_lt=dt.date(2000, 3, 15)), '(BEFORE 15-Mar-2000)') | ||
self.assertEqual(A(sent_date=dt.date(2000, 3, 15)), '(SENTON 15-Mar-2000)') | ||
self.assertEqual(A(sent_date_gte=dt.date(2000, 3, 15)), '(SENTSINCE 15-Mar-2000)') | ||
self.assertEqual(A(sent_date_lt=dt.date(2000, 3, 15)), '(SENTBEFORE 15-Mar-2000)') | ||
|
||
self.assertEqual(A(size_gt=1024), '(LARGER 1024)') | ||
self.assertEqual(A(size_lt=512), '(SMALLER 512)') | ||
|
||
self.assertEqual(A(new=True), '(NEW)') | ||
self.assertEqual(A(old=True), '(OLD)') | ||
self.assertEqual(A(recent=True), '(RECENT)') | ||
self.assertEqual(A(all=True), '(ALL)') | ||
|
||
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(gmail_label="TestLabel"), '(X-GM-LABELS "TestLabel")') | ||
|
||
def test_format_date(self): | ||
self.assertEqual(ParamConverter.format_date(dt.date(2000, 1, 15)), '15-Jan-2000') | ||
|
@@ -85,12 +85,12 @@ def test_logic_operators(self): | |
self.assertEqual(AND(text='hello', new=True), '(TEXT "hello" NEW)') | ||
self.assertEqual(OR(text='hello', new=True), '(OR TEXT "hello" NEW)') | ||
self.assertEqual(NOT(text='hello', new=True), 'NOT (TEXT "hello" NEW)') | ||
self.assertEqual(Q(AND(to='[email protected]'), AND(to='[email protected]')), '((TO "[email protected]") (TO "[email protected]"))') | ||
self.assertEqual(A(AND(to='[email protected]'), AND(to='[email protected]')), '((TO "[email protected]") (TO "[email protected]"))') | ||
self.assertEqual( | ||
OR(date=[dt.date(2019, 10, 1), dt.date(2019, 10, 10), dt.date(2019, 10, 15), dt.date(2019, 10, 20)]), | ||
'(OR OR OR ON 1-Oct-2019 ON 10-Oct-2019 ON 15-Oct-2019 ON 20-Oct-2019)') | ||
self.assertEqual( | ||
Q(OR(from_='[email protected]', text='"the text"'), NOT(OR(Q(answered=False), Q(new=True))), to='[email protected]'), | ||
A(OR(from_='[email protected]', text='"the text"'), NOT(OR(A(answered=False), A(new=True))), to='[email protected]'), | ||
'((OR FROM "[email protected]" TEXT "\\"the text\\"") NOT ((OR (UNANSWERED) (NEW))) TO "[email protected]")') | ||
|
||
def test_header(self): | ||
|
Q is missing here, causing old scripts that relay on Q to fail.