Skip to content

Commit f074f90

Browse files
authored
Merge pull request mjs#252 from NicolasLM/std-logging
Use the standard logging module
2 parents be31a0c + e07569b commit f074f90

File tree

5 files changed

+44
-76
lines changed

5 files changed

+44
-76
lines changed

doc/src/index.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,23 @@ TLS Support
258258
.. automodule:: imapclient.tls
259259
:members:
260260

261+
Logging
262+
~~~~~~~
263+
IMAPClient logs debug lines using the standard Python `logging module
264+
<https://docs.python.org/3/library/logging.html>`_. Its logger is
265+
``imapclient.*``.
266+
267+
A simple way to display log messages is to setup logging::
268+
269+
import logging
270+
271+
logging.basicConfig(
272+
format='%(asctime)s - %(levelname)s: %(message)s',
273+
level=logging.DEBUG
274+
)
275+
276+
For advanced usage please refer to the Python documentation.
277+
261278
Interactive Sessions
262279
--------------------
263280
When developing program using IMAPClient is it sometimes useful to

doc/src/releases.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
Changed
88
-------
99
- XXX Use built-in TLS when sensible.
10+
- Logs are now handled by the Python logging module. `debug` and `log_file`
11+
are not used anymore.
1012

1113
Other
1214
-----

imapclient/imapclient.py

Lines changed: 21 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import re
1313
from datetime import datetime, date
1414
from operator import itemgetter
15+
from logging import getLogger
1516

1617
from six import moves, iteritems, text_type, integer_types, PY3, binary_type, iterbytes
1718

@@ -28,6 +29,9 @@
2829
long = int # long is just int in python3
2930

3031

32+
logger = getLogger(__name__)
33+
imaplib_logger = getLogger('imapclient.imaplib')
34+
3135
__all__ = ['IMAPClient', 'DELETED', 'SEEN', 'ANSWERED', 'FLAGGED', 'DRAFT', 'RECENT']
3236

3337

@@ -104,16 +108,6 @@ class IMAPClient(object):
104108
system time). This attribute can be changed between ``fetch()``
105109
calls if required.
106110
107-
The *debug* property can be used to enable debug logging. It can
108-
be set to an integer from 0 to 5 where 0 disables debug output and
109-
5 enables full output with wire logging and parsing logs. ``True``
110-
and ``False`` can also be assigned where ``True`` sets debug level
111-
4.
112-
113-
By default, debug output goes to stderr. The *log_file* attribute
114-
can be assigned to an alternate file handle for writing debug
115-
output to.
116-
117111
"""
118112

119113
Error = imaplib.IMAP4.error
@@ -137,14 +131,19 @@ def __init__(self, host, port=None, use_uid=True, ssl=False, stream=False,
137131
self.stream = stream
138132
self.use_uid = use_uid
139133
self.folder_encode = True
140-
self.log_file = sys.stderr
141134
self.normalise_times = True
142135

143136
self._timeout = timeout
144137
self._starttls_done = False
145138
self._cached_capabilities = None
139+
146140
self._imap = self._create_IMAP4()
147-
self._imap._mesg = self._log # patch in custom debug log method
141+
logger.debug("Connected to host %s", self.host)
142+
143+
# Small hack to make imaplib log everything to its own logger
144+
self._imap.debug = 5
145+
self._imap._mesg = imaplib_logger.debug
146+
148147
self._idle_tag = None
149148

150149
self._set_timeout()
@@ -202,12 +201,14 @@ def login(self, username, password):
202201
"""Login using *username* and *password*, returning the
203202
server response.
204203
"""
205-
return self._command_and_check(
204+
rv = self._command_and_check(
206205
'login',
207206
to_unicode(username),
208207
to_unicode(password),
209208
unpack=True,
210209
)
210+
logger.info('Logged in as %s', username)
211+
return rv
211212

212213
def oauth2_login(self, user, access_token, mech='XOAUTH2', vendor=None):
213214
"""Authenticate using the OAUTH2 method.
@@ -234,6 +235,7 @@ def logout(self):
234235
"""
235236
typ, data = self._imap.logout()
236237
self._check_resp('BYE', 'logout', typ, data)
238+
logger.info('Logged out, connection closed')
237239
return data[0]
238240

239241
def shutdown(self):
@@ -243,6 +245,7 @@ def shutdown(self):
243245
this. The logout method also shutdown down the connection.
244246
"""
245247
self._imap.shutdown()
248+
logger.info('Connection closed')
246249

247250
def id_(self, parameters=None):
248251
"""Issue the ID command, returning a dict of server implementation
@@ -596,10 +599,7 @@ def idle_done(self):
596599
any). These are returned in parsed form as per
597600
``idle_check()``.
598601
"""
599-
if self.debug >= 4:
600-
self._log_ts()
601-
self._log_write('< DONE', end=True)
602-
602+
logger.debug('< DONE')
603603
self._imap.send(b'DONE\r\n')
604604
return self._consume_until_tagged_response(self._idle_tag, 'IDLE')
605605

@@ -1103,10 +1103,6 @@ def _raw_command(self, command, args):
11031103
*command* should be specified as bytes.
11041104
*args* should be specified as a list of bytes.
11051105
"""
1106-
if self.debug >= 4:
1107-
self._log_ts()
1108-
self._log_write('> ')
1109-
11101106
command = command.upper()
11111107

11121108
if isinstance(args, tuple):
@@ -1128,8 +1124,7 @@ def _raw_command(self, command, args):
11281124
if _is8bit(item):
11291125
if line:
11301126
out = b' '.join(line)
1131-
if self.debug >= 4:
1132-
self._log_write(out)
1127+
logger.debug('> %s', out)
11331128
self._imap.send(out)
11341129
line = []
11351130
self._send_literal(tag, item)
@@ -1140,22 +1135,18 @@ def _raw_command(self, command, args):
11401135

11411136
if line:
11421137
out = b' '.join(line)
1143-
if self.debug >= 4:
1144-
self._log_write(out)
1138+
logger.debug('> %s', out)
11451139
self._imap.send(out)
11461140

11471141
self._imap.send(b'\r\n')
1148-
if self.debug >= 4:
1149-
self._log_write("", end=True)
11501142

11511143
return self._imap._command_complete(to_unicode(command), tag)
11521144

11531145
def _send_literal(self, tag, item):
11541146
"""Send a single literal for the command with *tag*.
11551147
"""
11561148
out = b' {' + str(len(item)).encode('ascii') + b'}\r\n'
1157-
if self.debug >= 4:
1158-
self._log_write(out, end=True)
1149+
logger.debug(out)
11591150
self._imap.send(out)
11601151

11611152
# Wait for continuation response
@@ -1166,9 +1157,7 @@ def _send_literal(self, tag, item):
11661157
"unexpected response while waiting for continuation response: " +
11671158
repr(tagged_resp))
11681159

1169-
if self.debug >= 4:
1170-
self._log_write(" (literal) > ")
1171-
self._log_write(item)
1160+
logger.debug(" (literal) > %s", item)
11721161
self._imap.send(item)
11731162

11741163
def _command_and_check(self, command, *args, **kwargs):
@@ -1219,38 +1208,6 @@ def _filter_fetch_dict(self, fetch_dict, key):
12191208
return dict((msgid, data[key])
12201209
for msgid, data in iteritems(fetch_dict))
12211210

1222-
def __debug_get(self):
1223-
return self._imap.debug
1224-
1225-
def __debug_set(self, level):
1226-
if level is True:
1227-
level = 4
1228-
elif level is False:
1229-
level = 0
1230-
self._imap.debug = level
1231-
1232-
debug = property(__debug_get, __debug_set)
1233-
1234-
def _log_ts(self):
1235-
self.log_file.write(datetime.now().strftime('%M:%S.%f') + ' ')
1236-
1237-
def _log_write(self, text, end=False):
1238-
if isinstance(text, binary_type):
1239-
text = repr(text)
1240-
for i, c in enumerate(text):
1241-
if c in "\"'":
1242-
break
1243-
text = text[i + 1:-1]
1244-
self.log_file.write(text)
1245-
1246-
if end:
1247-
self.log_file.write('\n')
1248-
self.log_file.flush()
1249-
1250-
def _log(self, text):
1251-
self._log_ts()
1252-
self._log_write(text, end=True)
1253-
12541211
def _normalise_folder(self, folder_name):
12551212
if isinstance(folder_name, binary_type):
12561213
folder_name = folder_name.decode('ascii')

imapclient/test/test_imapclient.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import socket
99
import sys
1010
from datetime import datetime
11+
import logging
1112

1213
import six
1314

@@ -349,19 +350,11 @@ def fake_get_response():
349350

350351
class TestDebugLogging(IMAPClientTest):
351352

352-
def test_default_is_stderr(self):
353-
self.assertIs(self.client.log_file, sys.stderr)
354-
355353
def test_IMAP_is_patched(self):
356-
log = six.StringIO()
357-
self.client.log_file = log
358-
359-
self.client._log('one')
354+
log_stream = six.StringIO()
355+
logging.basicConfig(stream=log_stream, level=logging.DEBUG)
360356
self.client._imap._mesg('two')
361-
362-
output = log.getvalue()
363-
self.assertIn('one', output)
364-
self.assertIn('two', output)
357+
self.assertIn('DEBUG:imaplib:two', log_stream.getvalue())
365358

366359

367360
class TestTimeNormalisation(IMAPClientTest):

imapclient/test/testable_imapclient.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ def __init__(self, *args, **kwargs):
2424
self.use_uid = True
2525
self.sent = b'' # Accumulates what was given to send()
2626
self.tagged_commands = {}
27-
self.debug = 0
2827
self._starttls_done = False
2928

3029
def send(self, data):

0 commit comments

Comments
 (0)