Commit 9d8f48f9 authored by Aziz Berkay Yesilyurt's avatar Aziz Berkay Yesilyurt
Browse files

formatting

parent d9e3975d
Showing with 130 additions and 117 deletions
+130 -117
......@@ -2,4 +2,4 @@ from simplegmail.gmail import Gmail
from simplegmail import query
from simplegmail import label
__all__ = ['Gmail', 'query', 'label']
__all__ = ["Gmail", "query", "label"]
......@@ -6,12 +6,13 @@ This module contains the implementation of the Attachment object.
"""
import base64 # for base64.urlsafe_b64decode
import os # for os.path.exists
import os # for os.path.exists
from typing import Optional
class Attachment(object):
"""
The Attachment class for attachments to emails in your Gmail mailbox. This
The Attachment class for attachments to emails in your Gmail mailbox. This
class should not be manually instantiated.
Args:
......@@ -33,16 +34,16 @@ class Attachment(object):
data (bytes): The raw data of the file.
"""
def __init__(
self,
service: 'googleapiclient.discovery.Resource',
service: "googleapiclient.discovery.Resource",
user_id: str,
msg_id: str,
att_id: str,
filename: str,
filetype: str,
data: Optional[bytes] = None
data: Optional[bytes] = None,
) -> None:
self._service = service
self.user_id = user_id
......@@ -55,42 +56,42 @@ class Attachment(object):
def download(self) -> None:
"""
Downloads the data for an attachment if it does not exist.
Raises:
googleapiclient.errors.HttpError: There was an error executing the
googleapiclient.errors.HttpError: There was an error executing the
HTTP request.
"""
if self.data is not None:
return
res = self._service.users().messages().attachments().get(
userId=self.user_id, messageId=self.msg_id, id=self.id
).execute()
res = (
self._service.users()
.messages()
.attachments()
.get(userId=self.user_id, messageId=self.msg_id, id=self.id)
.execute()
)
data = res['data']
data = res["data"]
self.data = base64.urlsafe_b64decode(data)
def save(
self,
filepath: Optional[str] = None,
overwrite: bool = False
) -> None:
def save(self, filepath: Optional[str] = None, overwrite: bool = False) -> None:
"""
Saves the attachment. Downloads file data if not downloaded.
Args:
filepath: where to save the attachment. Default None, which uses
filepath: where to save the attachment. Default None, which uses
the filename stored.
overwrite: whether to overwrite existing files. Default False.
Raises:
FileExistsError: if the call would overwrite an existing file and
FileExistsError: if the call would overwrite an existing file and
overwrite is not set to True.
"""
if filepath is None:
filepath = self.filename
......@@ -103,6 +104,5 @@ class Attachment(object):
f"you would like to overwrite the file."
)
with open(filepath, 'wb') as f:
with open(filepath, "wb") as f:
f.write(self.data)
......@@ -10,9 +10,9 @@ class Label:
"""
A Gmail label object.
This class should not typically be constructed directly but rather returned
This class should not typically be constructed directly but rather returned
from Gmail.list_labels().
Args:
name: The name of the Label.
id: The ID of the label.
......@@ -28,7 +28,7 @@ class Label:
self.id = id
def __repr__(self) -> str:
return f'Label(name={self.name!r}, id={self.id!r})'
return f"Label(name={self.name!r}, id={self.id!r})"
def __str__(self) -> str:
return self.name
......@@ -46,16 +46,16 @@ class Label:
return False
INBOX = Label('INBOX', 'INBOX')
SPAM = Label('SPAM', 'SPAM')
TRASH = Label('TRASH', 'TRASH')
UNREAD = Label('UNREAD', 'UNREAD')
STARRED = Label('STARRED', 'STARRED')
SENT = Label('SENT', 'SENT')
IMPORTANT = Label('IMPORTANT', 'IMPORTANT')
DRAFT = Label('DRAFT', 'DRAFT')
PERSONAL = Label('CATEGORY_PERSONAL', 'CATEGORY_PERSONAL')
SOCIAL = Label('CATEGORY_SOCIAL', 'CATEGORY_SOCIAL')
PROMOTIONS = Label('CATEGORY_PROMOTIONS', 'CATEGORY_PROMOTIONS')
UPDATES = Label('CATEGORY_UPDATES', 'CATEGORY_UPDATES')
FORUMS = Label('CATEGORY_FORUMS', 'CATEGORY_FORUMS')
INBOX = Label("INBOX", "INBOX")
SPAM = Label("SPAM", "SPAM")
TRASH = Label("TRASH", "TRASH")
UNREAD = Label("UNREAD", "UNREAD")
STARRED = Label("STARRED", "STARRED")
SENT = Label("SENT", "SENT")
IMPORTANT = Label("IMPORTANT", "IMPORTANT")
DRAFT = Label("DRAFT", "DRAFT")
PERSONAL = Label("CATEGORY_PERSONAL", "CATEGORY_PERSONAL")
SOCIAL = Label("CATEGORY_SOCIAL", "CATEGORY_SOCIAL")
PROMOTIONS = Label("CATEGORY_PROMOTIONS", "CATEGORY_PROMOTIONS")
UPDATES = Label("CATEGORY_UPDATES", "CATEGORY_UPDATES")
FORUMS = Label("CATEGORY_FORUMS", "CATEGORY_FORUMS")
......@@ -61,8 +61,8 @@ class Message(object):
def __init__(
self,
service: 'googleapiclient.discovery.Resource',
creds: 'oauth2client.client.OAuth2Credentials',
service: "googleapiclient.discovery.Resource",
creds: "oauth2client.client.OAuth2Credentials",
user_id: str,
msg_id: str,
thread_id: str,
......@@ -77,7 +77,7 @@ class Message(object):
attachments: Optional[List[Attachment]] = None,
headers: Optional[dict] = None,
cc: Optional[List[str]] = None,
bcc: Optional[List[str]] = None
bcc: Optional[List[str]] = None,
) -> None:
self._service = service
self.creds = creds
......@@ -98,7 +98,7 @@ class Message(object):
self.bcc = bcc or []
@property
def service(self) -> 'googleapiclient.discovery.Resource':
def service(self) -> "googleapiclient.discovery.Resource":
if self.creds.access_token_expired:
self.creds.refresh(Http())
......@@ -107,9 +107,7 @@ class Message(object):
def __repr__(self) -> str:
"""Represents the object by its sender, recipient, and id."""
return (
f'Message(to: {self.recipient}, from: {self.sender}, id: {self.id})'
)
return f"Message(to: {self.recipient}, from: {self.sender}, id: {self.id})"
def mark_as_read(self) -> None:
"""
......@@ -238,19 +236,26 @@ class Message(object):
"""
try:
res = self._service.users().messages().trash(
userId=self.user_id, id=self.id,
).execute()
res = (
self._service.users()
.messages()
.trash(
userId=self.user_id,
id=self.id,
)
.execute()
)
except HttpError as error:
# Pass error along
raise error
else:
assert label.TRASH in res['labelIds'], \
f'An error occurred in a call to `trash`.'
assert (
label.TRASH in res["labelIds"]
), f"An error occurred in a call to `trash`."
self.label_ids = res['labelIds']
self.label_ids = res["labelIds"]
def untrash(self) -> None:
"""
......@@ -263,19 +268,26 @@ class Message(object):
"""
try:
res = self._service.users().messages().untrash(
userId=self.user_id, id=self.id,
).execute()
res = (
self._service.users()
.messages()
.untrash(
userId=self.user_id,
id=self.id,
)
.execute()
)
except HttpError as error:
# Pass error along
raise error
else:
assert label.TRASH not in res['labelIds'], \
f'An error occurred in a call to `untrash`.'
assert (
label.TRASH not in res["labelIds"]
), f"An error occurred in a call to `untrash`."
self.label_ids = res['labelIds']
self.label_ids = res["labelIds"]
def move_from_inbox(self, to: Union[Label, str]) -> None:
"""
......@@ -355,7 +367,7 @@ class Message(object):
def modify_labels(
self,
to_add: Union[Label, str, List[Label], List[str]],
to_remove: Union[Label, str, List[Label], List[str]]
to_remove: Union[Label, str, List[Label], List[str]],
) -> None:
"""
Adds or removes the specified label.
......@@ -377,26 +389,32 @@ class Message(object):
to_remove = [to_remove]
try:
res = self._service.users().messages().modify(
userId=self.user_id, id=self.id,
body=self._create_update_labels(to_add, to_remove)
).execute()
res = (
self._service.users()
.messages()
.modify(
userId=self.user_id,
id=self.id,
body=self._create_update_labels(to_add, to_remove),
)
.execute()
)
except HttpError as error:
# Pass along error
raise error
else:
assert all([lbl in res['labelIds'] for lbl in to_add]) \
and all([lbl not in res['labelIds'] for lbl in to_remove]), \
'An error occurred while modifying message label.'
assert all([lbl in res["labelIds"] for lbl in to_add]) and all(
[lbl not in res["labelIds"] for lbl in to_remove]
), "An error occurred while modifying message label."
self.label_ids = res['labelIds']
self.label_ids = res["labelIds"]
def _create_update_labels(
self,
to_add: Union[List[Label], List[str]] = None,
to_remove: Union[List[Label], List[str]] = None
to_remove: Union[List[Label], List[str]] = None,
) -> dict:
"""
Creates an object for updating message label.
......@@ -417,10 +435,10 @@ class Message(object):
to_remove = []
return {
'addLabelIds': [
"addLabelIds": [
lbl.id if isinstance(lbl, Label) else lbl for lbl in to_add
],
'removeLabelIds': [
"removeLabelIds": [
lbl.id if isinstance(lbl, Label) else lbl for lbl in to_remove
]
],
}
......@@ -159,20 +159,20 @@ def construct_query(*query_dicts, **query_terms) -> str:
terms = []
for key, val in query_terms.items():
exclude = False
if key.startswith('exclude'):
if key.startswith("exclude"):
exclude = True
key = key[len('exclude_'):]
key = key[len("exclude_") :]
query_fn = globals()[f"_{key}"]
conjunction = _and if isinstance(val, tuple) else _or
if key in ['newer_than', 'older_than', 'near_words']:
if key in ["newer_than", "older_than", "near_words"]:
if isinstance(val[0], (tuple, list)):
term = conjunction([query_fn(*v) for v in val])
else:
term = query_fn(*val)
elif key == 'labels':
elif key == "labels":
if isinstance(val[0], (tuple, list)):
term = conjunction([query_fn(labels) for labels in val])
else:
......@@ -225,7 +225,7 @@ def _or(queries: List[str]) -> str:
if len(queries) == 1:
return queries[0]
return '{' + ' '.join(queries) + '}'
return "{" + " ".join(queries) + "}"
def _exclude(term: str) -> str:
......@@ -240,7 +240,7 @@ def _exclude(term: str) -> str:
"""
return f'-{term}'
return f"-{term}"
def _sender(sender: str) -> str:
......@@ -255,7 +255,7 @@ def _sender(sender: str) -> str:
"""
return f'from:{sender}'
return f"from:{sender}"
def _recipient(recipient: str) -> str:
......@@ -270,7 +270,7 @@ def _recipient(recipient: str) -> str:
"""
return f'to:{recipient}'
return f"to:{recipient}"
def _subject(subject: str) -> str:
......@@ -285,7 +285,7 @@ def _subject(subject: str) -> str:
"""
return f'subject:{subject}'
return f"subject:{subject}"
def _labels(labels: Union[List[str], str]) -> str:
......@@ -320,7 +320,7 @@ def _label(label: str) -> str:
"""
return f'label:{label}'
return f"label:{label}"
def _spec_attachment(name_or_type: str) -> str:
......@@ -336,7 +336,7 @@ def _spec_attachment(name_or_type: str) -> str:
"""
return f'filename:{name_or_type}'
return f"filename:{name_or_type}"
def _exact_phrase(phrase: str) -> str:
......@@ -357,31 +357,31 @@ def _exact_phrase(phrase: str) -> str:
def _starred() -> str:
"""Returns a query term matching messages that are starred."""
return 'is:starred'
return "is:starred"
def _snoozed() -> str:
"""Returns a query term matching messages that are snoozed."""
return 'is:snoozed'
return "is:snoozed"
def _unread() -> str:
"""Returns a query term matching messages that are unread."""
return 'is:unread'
return "is:unread"
def _read() -> str:
"""Returns a query term matching messages that are read."""
return 'is:read'
return "is:read"
def _important() -> str:
"""Returns a query term matching messages that are important."""
return 'is:important'
return "is:important"
def _cc(recipient: str) -> str:
......@@ -397,7 +397,7 @@ def _cc(recipient: str) -> str:
"""
return f'cc:{recipient}'
return f"cc:{recipient}"
def _bcc(recipient: str) -> str:
......@@ -413,7 +413,7 @@ def _bcc(recipient: str) -> str:
"""
return f'bcc:{recipient}'
return f"bcc:{recipient}"
def _after(date: str) -> str:
......@@ -428,7 +428,7 @@ def _after(date: str) -> str:
"""
return f'after:{date}'
return f"after:{date}"
def _before(date: str) -> str:
......@@ -443,7 +443,7 @@ def _before(date: str) -> str:
"""
return f'before:{date}'
return f"before:{date}"
def _older_than(number: int, unit: str) -> str:
......@@ -459,7 +459,7 @@ def _older_than(number: int, unit: str) -> str:
"""
return f'older_than:{number}{unit[0]}'
return f"older_than:{number}{unit[0]}"
def _newer_than(number: int, unit: str) -> str:
......@@ -475,15 +475,10 @@ def _newer_than(number: int, unit: str) -> str:
"""
return f'newer_than:{number}{unit[0]}'
return f"newer_than:{number}{unit[0]}"
def _near_words(
first: str,
second: str,
distance: int,
exact: bool = False
) -> str:
def _near_words(first: str, second: str, distance: int, exact: bool = False) -> str:
"""
Returns a query term matching messages that two words within a certain
distance of each other.
......@@ -499,7 +494,7 @@ def _near_words(
"""
query = f'{first} AROUND {distance} {second}'
query = f"{first} AROUND {distance} {second}"
if exact:
query = '"' + query + '"'
......@@ -509,7 +504,7 @@ def _near_words(
def _attachment() -> str:
"""Returns a query term matching messages that have attachments."""
return 'has:attachment'
return "has:attachment"
def _drive() -> str:
......@@ -518,7 +513,7 @@ def _drive() -> str:
"""
return 'has:drive'
return "has:drive"
def _docs() -> str:
......@@ -527,7 +522,7 @@ def _docs() -> str:
"""
return 'has:document'
return "has:document"
def _sheets() -> str:
......@@ -536,7 +531,7 @@ def _sheets() -> str:
"""
return 'has:spreadsheet'
return "has:spreadsheet"
def _slides() -> str:
......@@ -545,7 +540,7 @@ def _slides() -> str:
"""
return 'has:presentation'
return "has:presentation"
def _list(list_name: str) -> str:
......@@ -560,7 +555,7 @@ def _list(list_name: str) -> str:
"""
return f'list:{list_name}'
return f"list:{list_name}"
def _in(folder_name: str) -> str:
......@@ -575,7 +570,7 @@ def _in(folder_name: str) -> str:
"""
return f'in:{folder_name}'
return f"in:{folder_name}"
def _delivered_to(address: str) -> str:
......@@ -590,7 +585,7 @@ def _delivered_to(address: str) -> str:
"""
return f'deliveredto:{address}'
return f"deliveredto:{address}"
def _category(category: str) -> str:
......@@ -605,7 +600,7 @@ def _category(category: str) -> str:
"""
return f'category:{category}'
return f"category:{category}"
def _larger(size: str) -> str:
......@@ -621,7 +616,7 @@ def _larger(size: str) -> str:
"""
return f'larger:{size}'
return f"larger:{size}"
def _smaller(size: str) -> str:
......@@ -637,7 +632,7 @@ def _smaller(size: str) -> str:
"""
return f'smaller:{size}'
return f"smaller:{size}"
def _id(message_id: str) -> str:
......@@ -652,7 +647,7 @@ def _id(message_id: str) -> str:
"""
return f'rfc822msgid:{message_id}'
return f"rfc822msgid:{message_id}"
def _has(attribute: str) -> str:
......@@ -667,4 +662,4 @@ def _has(attribute: str) -> str:
"""
return f'has:{attribute}'
return f"has:{attribute}"
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment