Skip to content

Commit

Permalink
chore: setup black for CI
Browse files Browse the repository at this point in the history
Commit f072340 ran `black` on the
code base. But since it wasn't being enforced in the CI, it has
drifted away from `black` standarized formatting.

Did the following:
  * Added a Github CI job to run `black`
  * Added a `black` tox environment
  * Ran `black` on the code
  * Added the `requirements-dev.txt` file

This has been setup so that it should be easy to add other
checkers/linters in the future as desired.
  • Loading branch information
JohnVillalovos committed Jun 23, 2022
1 parent 8892ff1 commit 65409e1
Show file tree
Hide file tree
Showing 13 changed files with 132 additions and 82 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/lint.yml
@@ -0,0 +1,30 @@
name: Lint

# If a pull-request is pushed then cancel all previously running jobs related
# to that pull-request
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true

on:
push:
branches:
- master
pull_request:
branches:
- master

env:
PY_COLORS: 1

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-python@v3
- run: pip install --upgrade tox
- name: Run black code formatter (https://black.readthedocs.io/en/stable/)
run: tox -e black -- --check
8 changes: 4 additions & 4 deletions doc/src/conf.py
Expand Up @@ -38,8 +38,8 @@
master_doc = "index"

# General information about the project.
project = u"IMAPClient"
copyright = u"2014, Menno Smits"
project = "IMAPClient"
copyright = "2014, Menno Smits"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand Down Expand Up @@ -179,7 +179,7 @@
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
("index", "IMAPClient.tex", u"IMAPClient Documentation", u"Menno Smits", "manual"),
("index", "IMAPClient.tex", "IMAPClient Documentation", "Menno Smits", "manual"),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down Expand Up @@ -210,4 +210,4 @@

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [("index", "imapclient", u"IMAPClient Documentation", [u"Menno Smits"], 1)]
man_pages = [("index", "imapclient", "IMAPClient Documentation", ["Menno Smits"], 1)]
5 changes: 1 addition & 4 deletions examples/idle_selector_example.py
Expand Up @@ -13,10 +13,7 @@
server.login(USERNAME, PASSWORD)
server.select_folder("INBOX", readonly=True)
server.idle()
print(
"Connection is now in IDLE mode,"
" send yourself an email or quit with ^c"
)
print("Connection is now in IDLE mode," " send yourself an email or quit with ^c")
try:
with DefaultSelector() as selector:
selector.register(server.socket(), EVENT_READ, None)
Expand Down
3 changes: 2 additions & 1 deletion imapclient/config.py
Expand Up @@ -131,7 +131,8 @@ def refresh_oauth2_token(hostname, client_id, client_secret, refresh_token):
grant_type=b"refresh_token",
)
response = urllib.request.urlopen(
url, urllib.parse.urlencode(post).encode("ascii")).read()
url, urllib.parse.urlencode(post).encode("ascii")
).read()
return json.loads(response.decode("ascii"))["access_token"]


Expand Down
1 change: 1 addition & 0 deletions imapclient/exceptions.py
Expand Up @@ -4,6 +4,7 @@
# To ensure backward compatibility, we "rename" the imaplib general
# exception class, so we can catch its exceptions without having to
# deal with it in IMAPClient codebase

IMAPClientError = imaplib.IMAP4.error
IMAPClientAbortError = imaplib.IMAP4.abort
IMAPClientReadOnlyError = imaplib.IMAP4.readonly
Expand Down
31 changes: 15 additions & 16 deletions imapclient/imapclient.py
Expand Up @@ -79,21 +79,21 @@
imaplib.Commands["MOVE"] = ("AUTH", "SELECTED")

# System flags
DELETED = br"\Deleted"
SEEN = br"\Seen"
ANSWERED = br"\Answered"
FLAGGED = br"\Flagged"
DRAFT = br"\Draft"
RECENT = br"\Recent" # This flag is read-only
DELETED = rb"\Deleted"
SEEN = rb"\Seen"
ANSWERED = rb"\Answered"
FLAGGED = rb"\Flagged"
DRAFT = rb"\Draft"
RECENT = rb"\Recent" # This flag is read-only

# Special folders, see RFC6154
# \Flagged is omitted because it is the same as the flag defined above
ALL = br"\All"
ARCHIVE = br"\Archive"
DRAFTS = br"\Drafts"
JUNK = br"\Junk"
SENT = br"\Sent"
TRASH = br"\Trash"
ALL = rb"\All"
ARCHIVE = rb"\Archive"
DRAFTS = rb"\Drafts"
JUNK = rb"\Junk"
SENT = rb"\Sent"
TRASH = rb"\Trash"

# Personal namespaces that are common among providers
# used as a fallback when the server does not support the NAMESPACE capability
Expand All @@ -108,7 +108,7 @@
JUNK: ("Junk", "Spam"),
}

_RE_SELECT_RESPONSE = re.compile(br"\[(?P<key>[A-Z-]+)( \((?P<data>.*)\))?\]")
_RE_SELECT_RESPONSE = re.compile(rb"\[(?P<key>[A-Z-]+)( \((?P<data>.*)\))?\]")


class Namespace(tuple):
Expand Down Expand Up @@ -1266,9 +1266,7 @@ def get_gmail_labels(self, messages):
"""
response = self.fetch(messages, [b"X-GM-LABELS"])
response = self._filter_fetch_dict(response, b"X-GM-LABELS")
return {
msg: utf7_decode_sequence(labels) for msg, labels in response.items()
}
return {msg: utf7_decode_sequence(labels) for msg, labels in response.items()}

def add_gmail_labels(self, messages, labels, silent=False):
"""Add *labels* to *messages* in the currently selected folder.
Expand Down Expand Up @@ -1420,6 +1418,7 @@ def multiappend(self, folder, msgs):
Returns the APPEND response from the server.
"""

def chunks():
for m in msgs:
if isinstance(m, dict):
Expand Down
4 changes: 2 additions & 2 deletions livetest.py
Expand Up @@ -303,7 +303,7 @@ def test_select_read_only(self):
def test_list_folders(self):
some_folders = ["simple", b"simple2", "L\xffR"]
if not self.is_fastmail():
some_folders.extend([r'test"folder"', br"foo\bar"])
some_folders.extend([r'test"folder"', rb"foo\bar"])
some_folders = self.add_prefix_to_folders(some_folders)
for name in some_folders:
self.client.create_folder(name)
Expand All @@ -329,7 +329,7 @@ def test_xlist(self):

foundInbox = False
for flags, _, _ in result:
if br"\INBOX" in [flag.upper() for flag in flags]:
if rb"\INBOX" in [flag.upper() for flag in flags]:
foundInbox = True
break
if not foundInbox:
Expand Down
2 changes: 2 additions & 0 deletions requirements-dev.txt
@@ -0,0 +1,2 @@
sphinx
black==22.3.0
3 changes: 2 additions & 1 deletion setup.py
Expand Up @@ -46,7 +46,8 @@
author_email=info["author_email"],
license="http://en.wikipedia.org/wiki/BSD_licenses",
url="https://github.com/mjs/imapclient/",
download_url="http://menno.io/projects/IMAPClient/IMAPClient-%s.zip" % info["version"],
download_url="http://menno.io/projects/IMAPClient/IMAPClient-%s.zip"
% info["version"],
packages=["imapclient"],
package_data=dict(imapclient=["examples/*.py"]),
extras_require={"doc": doc_deps},
Expand Down
91 changes: 53 additions & 38 deletions tests/test_imapclient.py
Expand Up @@ -205,11 +205,11 @@ def test_funky_characters(self):
def test_quoted_specials(self):
folders = self.client._proc_folder_list(
[
br'(\HasNoChildren) "/" "Test \"Folder\""',
br'(\HasNoChildren) "/" "Left\"Right"',
br'(\HasNoChildren) "/" "Left\\Right"',
br'(\HasNoChildren) "/" "\"Left Right\""',
br'(\HasNoChildren) "/" "\"Left\\Right\""',
rb'(\HasNoChildren) "/" "Test \"Folder\""',
rb'(\HasNoChildren) "/" "Left\"Right"',
rb'(\HasNoChildren) "/" "Left\\Right"',
rb'(\HasNoChildren) "/" "\"Left Right\""',
rb'(\HasNoChildren) "/" "\"Left\\Right\""',
]
)
self.assertEqual(
Expand All @@ -228,9 +228,9 @@ def test_empty_response(self):

def test_blanks(self):
folders = self.client._proc_folder_list(
["", None, br'(\HasNoChildren) "/" "last"']
["", None, rb'(\HasNoChildren) "/" "last"']
)
self.assertEqual(folders, [((br"\HasNoChildren",), b"/", "last")])
self.assertEqual(folders, [((rb"\HasNoChildren",), b"/", "last")])


class TestFindSpecialFolder(IMAPClientTest):
Expand Down Expand Up @@ -284,15 +284,15 @@ def test_normal(self):
self.client._command_and_check = Mock()
self.client._imap.untagged_responses = {
b"exists": [b"3"],
b"FLAGS": [br"(\Flagged \Deleted abc [foo]/bar def)"],
b"FLAGS": [rb"(\Flagged \Deleted abc [foo]/bar def)"],
b"HIGHESTMODSEQ": [b"127110"],
b"OK": [
br"[PERMANENTFLAGS (\Flagged \Deleted abc [foo]/bar def \*)] Flags permitted.",
rb"[PERMANENTFLAGS (\Flagged \Deleted abc [foo]/bar def \*)] Flags permitted.",
b"[UIDVALIDITY 631062293] UIDs valid.",
b"[UIDNEXT 1281] Predicted next UID.",
b"[HIGHESTMODSEQ 127110]",
],
b"PERMANENTFLAGS": [br"(\Flagged \Deleted abc [foo"],
b"PERMANENTFLAGS": [rb"(\Flagged \Deleted abc [foo"],
b"READ-WRITE": [b""],
b"RECENT": [b"0"],
b"UIDNEXT": [b"1281"],
Expand All @@ -314,14 +314,14 @@ def test_normal(self):
b"UIDNEXT": 1281,
b"UIDVALIDITY": 631062293,
b"HIGHESTMODSEQ": 127110,
b"FLAGS": (br"\Flagged", br"\Deleted", b"abc", b"[foo]/bar", b"def"),
b"FLAGS": (rb"\Flagged", rb"\Deleted", b"abc", b"[foo]/bar", b"def"),
b"PERMANENTFLAGS": (
br"\Flagged",
br"\Deleted",
rb"\Flagged",
rb"\Deleted",
b"abc",
b"[foo]/bar",
b"def",
br"\*",
rb"\*",
),
b"READ-WRITE": True,
b"OTHER": [b"blah"],
Expand Down Expand Up @@ -380,30 +380,38 @@ def test_multiappend(self):
def test_multiappend_with_flags_and_internaldate(self):
self.client._cached_capabilities = (b"MULTIAPPEND",)
self.client._raw_command = Mock()
self.client.multiappend("foobar", [
{
"msg": "msg1",
"flags": ["FLAG", "WAVE"],
"date": datetime(2009, 4, 5, 11, 0, 5, 0, FixedOffset(2 * 60)),
},
{
"msg": "msg2",
"flags": ["FLAG", "WAVE"],
},
{
"msg": "msg3",
"date": datetime(2009, 4, 5, 11, 0, 5, 0, FixedOffset(2 * 60)),
}])
self.client.multiappend(
"foobar",
[
{
"msg": "msg1",
"flags": ["FLAG", "WAVE"],
"date": datetime(2009, 4, 5, 11, 0, 5, 0, FixedOffset(2 * 60)),
},
{
"msg": "msg2",
"flags": ["FLAG", "WAVE"],
},
{
"msg": "msg3",
"date": datetime(2009, 4, 5, 11, 0, 5, 0, FixedOffset(2 * 60)),
},
],
)

self.client._raw_command.assert_called_once_with(
b"APPEND", [b'"foobar"',
b'(FLAG WAVE)',
b'"05-Apr-2009 11:00:05 +0200"',
_literal(b"msg1"),
b'(FLAG WAVE)',
_literal(b"msg2"),
b'"05-Apr-2009 11:00:05 +0200"',
_literal(b"msg3")], uid=False
b"APPEND",
[
b'"foobar"',
b"(FLAG WAVE)",
b'"05-Apr-2009 11:00:05 +0200"',
_literal(b"msg1"),
b"(FLAG WAVE)",
_literal(b"msg2"),
b'"05-Apr-2009 11:00:05 +0200"',
_literal(b"msg3"),
],
uid=False,
)


Expand Down Expand Up @@ -1000,13 +1008,19 @@ def test_literal_plus_multiple_literals(self):
self.client._cached_capabilities = (b"LITERAL+",)

typ, data = self.client._raw_command(
b"APPEND", [b"\xff", _literal(b"hello"), b"TEXT", _literal(b"test")], uid=False
b"APPEND",
[b"\xff", _literal(b"hello"), b"TEXT", _literal(b"test")],
uid=False,
)
self.assertEqual(typ, "OK")
self.assertEqual(data, ["done"])
self.assertEqual(
self.client._imap.sent,
b"tag APPEND {1+}\r\n" b"\xff {5+}\r\n" b"hello" b" TEXT {4+}\r\n" b"test\r\n",
b"tag APPEND {1+}\r\n"
b"\xff {5+}\r\n"
b"hello"
b" TEXT {4+}\r\n"
b"test\r\n",
)

def test_complex(self):
Expand Down Expand Up @@ -1090,6 +1104,7 @@ def test_tagged_response_with_parse_error(self):
with self.assertRaises(ProtocolError):
client._consume_until_tagged_response(sentinel.tag, b"IDLE")


class TestSocket(IMAPClientTest):
def test_issues_warning_for_deprecating_sock_property(self):
mock_sock = Mock()
Expand Down
10 changes: 5 additions & 5 deletions tests/test_response_lexer.py
Expand Up @@ -32,12 +32,12 @@ def test_unterminated_strings(self):
self.check_error([b'"aaa bbb'], message)

def test_escaping(self):
self.check([br'"aaa\"bbb"'], [br'"aaa"bbb"'])
self.check([br'"aaa\\bbb"'], [br'"aaa\bbb"'])
self.check([br'"aaa\\bbb \"\""'], [br'"aaa\bbb """'])
self.check([rb'"aaa\"bbb"'], [rb'"aaa"bbb"'])
self.check([rb'"aaa\\bbb"'], [rb'"aaa\bbb"'])
self.check([rb'"aaa\\bbb \"\""'], [rb'"aaa\bbb """'])

def test_invalid_escape(self):
self.check([br'"aaa\Zbbb"'], [br'"aaa\Zbbb"'])
self.check([rb'"aaa\Zbbb"'], [rb'"aaa\Zbbb"'])

def test_lists(self):
self.check([b"()"], [b"(", b")"])
Expand All @@ -59,7 +59,7 @@ def test_square_brackets(self):
self.check([b"aaa [bbb]"], [b"aaa", b"[bbb]"])

def test_no_escaping_in_square_brackets(self):
self.check([br"[aaa\\bbb]"], [br"[aaa\\bbb]"])
self.check([rb"[aaa\\bbb]"], [rb"[aaa\\bbb]"])

def test_unmatched_square_brackets(self):
message = "No closing ']'"
Expand Down
12 changes: 6 additions & 6 deletions tests/test_response_parser.py
Expand Up @@ -30,7 +30,7 @@ class TestParseResponse(unittest.TestCase):
def test_unquoted(self):
self._test(b"FOO", b"FOO")
self._test(b"F.O:-O_0;", b"F.O:-O_0;")
self._test(br"\Seen", br"\Seen")
self._test(rb"\Seen", rb"\Seen")

def test_string(self):
self._test(b'"TEST"', b"TEST")
Expand Down Expand Up @@ -159,9 +159,9 @@ def test_literal_with_more(self):
self._test(response, (12, b"foo", literal_text))

def test_quoted_specials(self):
self._test(br'"\"foo bar\""', b'"foo bar"')
self._test(br'"foo \"bar\""', b'foo "bar"')
self._test(br'"foo\\bar"', br"foo\bar")
self._test(rb'"\"foo bar\""', b'"foo bar"')
self._test(rb'"foo \"bar\""', b'foo "bar"')
self._test(rb'"foo\\bar"', rb"foo\bar")

def test_square_brackets(self):
self._test(b"foo[bar rrr]", b"foo[bar rrr]")
Expand Down Expand Up @@ -263,8 +263,8 @@ def test_bad_UID(self):

def test_FLAGS(self):
self.assertEqual(
parse_fetch_response([br"23 (FLAGS (\Seen Stuff))"]),
{23: {b"SEQ": 23, b"FLAGS": (br"\Seen", b"Stuff")}},
parse_fetch_response([rb"23 (FLAGS (\Seen Stuff))"]),
{23: {b"SEQ": 23, b"FLAGS": (rb"\Seen", b"Stuff")}},
)

def test_multiple_messages(self):
Expand Down

0 comments on commit 65409e1

Please sign in to comment.