Skip to content

Commit e29585a

Browse files
author
Menno Smits
committed
Merged 0.12 changes to stable
--HG-- branch : stable
2 parents 934efed + 6a98e27 commit e29585a

29 files changed

+1547
-848
lines changed

.hgignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ doc/html/
66
.tox/
77
^mock-.+.egg.*
88
^setuptools-.+
9+
^unittest2-.+
10+
^six-.+
11+
^argparse-.+\.egg$

NEWS.rst

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,53 @@
1+
======
2+
0.12
3+
======
4+
5+
Fixed unicode handling [API CHANGE]
6+
-----------------------------------
7+
During the work to support Python 3, IMAPClient was changed to do
8+
return unicode for most responses. This was a bad decision, especially
9+
because it effectively breaks content that uses multiple encodings
10+
(e.g. RFC822 responses). This release includes major changes so that
11+
most responses are returned as bytes (Python 3) or str (Python
12+
2). This means that correct handling of response data is now possible
13+
by code using IMAPClient.
14+
15+
Folder name handling has also been cleaned up as part of this work. If
16+
the ``folder_encode`` attribute is ``True`` (the default) then folder
17+
names will **always** be returned as unicode. If ``folder_encode`` is
18+
False then folder names will always be returned as bytes/strs.
19+
20+
Code using IMAPClient will most likely need to be updated to account
21+
these unicode handling changes.
22+
23+
Many thanks to Inbox (https://www.inboxapp.com/) for sponsoring this
24+
work.
25+
26+
Extra __init__ keyword args are passed through [NEW]
27+
----------------------------------------------------
28+
Any unused keyword arguments passed to the IMAPClient initialiser will
29+
now be passed through to the underlying imaplib IMAP4, IMAP4_SSL or
30+
IMAP4_stream class. This is specifically to allow the use of imaplib
31+
features that control certificate validation (if available with the
32+
version of Python being used).
33+
34+
Thanks to Chris Arndt for this change.
35+
36+
MODSEQ parts in SEARCH responses are now handled
37+
------------------------------------------------
38+
If the CONDSTORE extension is supported by a server and a MODSEQ
39+
criteria was used with search(), a TypeError could occur. This has now
40+
been fixed and the MODSEQ value returned by the server is now
41+
available via an attribute on the returned list of ids.
42+
43+
Minor Changes
44+
-------------
45+
* Small tweaks to support Python 3.4.
46+
* The deprecated get_folder_delimiter() method has been removed.
47+
* More control over OAUTH2 parameters. Thanks to Phil Peterson for
48+
this.
49+
* Fixed livetest/interact OAUTH handling under Python 3.
50+
151
========
252
0.11.1
353
========

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Features:
1717
- Convenience methods are provided for commonly used functionality.
1818
- Exceptions are raised when errors occur.
1919

20-
Python versions 2.6, 2.7, 3.2 and 3.3 are officially supported.
20+
Python versions 2.6, 2.7, 3.2, 3.3 and 3.4 are officially supported.
2121

2222
Why IMAPClient?
2323
---------------

THANKS

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,13 @@ John Louis del Rosario
4444
Gmail search support (X-GM-RAW).
4545

4646
Naveen Nathan
47-
ENVELOPE FETCH response parsing
48-
47+
ENVELOPE FETCH response parsing.
48+
49+
Chris Arndt
50+
Pass-through imaplib kwargs.
51+
52+
Phil Peterson
53+
OAUTH2 improvements.
4954

5055
Bug Reports
5156
------------
@@ -59,7 +64,6 @@ Erik Quaeghebeur
5964
Benjamin Morrise
6065
Brandon Rhodes
6166

62-
6367
External Projects
6468
-----------------
6569
IMAPClient uses Michael's excellent 'Mock' package.
@@ -69,5 +73,7 @@ IMAPClient includes Benjamin Peterson's 'six' library to help achieve
6973
cross-version Python compatibility.
7074
(https://pypi.python.org/pypi/six)
7175

76+
External Contributions
77+
----------------------
7278
The project was started while the author was working at NetBox Blue.
73-
(http://netboxblue.com/)
79+
(http://netboxblue.com/)

doc/src/conf.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717

1818
# Add any Sphinx extension module names here, as strings. They can be extensions
1919
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
20-
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
20+
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode',
21+
'sphinx.ext.intersphinx']
22+
23+
intersphinx_mapping = {'python':('http://docs.python.org/3', None)}
2124

2225
# Add any paths that contain templates here, relative to this directory.
2326
templates_path = ['_templates']

doc/src/index.rst

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ explains IMAP in detail. Other RFCs also apply to various extensions
3131
to the base protocol. These are referred to in the documentation below
3232
where relevant.
3333

34-
Python versions 2.6, 2.7, 3.2 and 3.3 are officially supported.
34+
Python versions 2.6, 2.7, 3.2, 3.3 and 3.4 are officially supported.
3535

3636
A Simple Example
3737
----------------
@@ -53,18 +53,18 @@ The output from this example could look something like this
5353
75 messages that aren't deleted
5454

5555
Messages:
56-
ID 38273: 1775 bytes, flags=('NonJunk',)
57-
ID 36459: 2833 bytes, flags=('\\Flagged', '\\Seen')
58-
ID 34693: 2874 bytes, flags=('\\Flagged', '\\Seen')
59-
ID 38066: 5368 bytes, flags=('\\Flagged', '\\Seen')
60-
ID 38154: 9079 bytes, flags=('\\Seen', 'NonJunk')
61-
ID 14099: 3322 bytes, flags=('\\Flagged', '\\Seen', '$Label1')
62-
ID 34196: 9163 bytes, flags=('\\Answered', '\\Seen')
63-
ID 35349: 4266 bytes, flags=('\\Flagged', '\\Seen')
64-
ID 29335: 5617 bytes, flags=('\\Flagged', '\\Seen', 'NonJunk')
65-
ID 38041: 7649 bytes, flags=('\\Seen', 'NonJunk')
66-
ID 22310: 976108 bytes, flags=('\\Flagged', '\\Seen', '$Label1')
67-
ID 6439: 3792 bytes, flags=('\\Flagged', '\\Seen', '$Label1', 'Junk')
56+
ID 38273: 1775 bytes, flags=(b'NonJunk',)
57+
ID 36459: 2833 bytes, flags=(b'\\Flagged', v'\\Seen')
58+
ID 34693: 2874 bytes, flags=(b'\\Flagged', v'\\Seen')
59+
ID 38066: 5368 bytes, flags=(b'\\Flagged', v'\\Seen')
60+
ID 38154: 9079 bytes, flags=(b'\\Seen', b'NonJunk')
61+
ID 14099: 3322 bytes, flags=(b'\\Flagged', b'\\Seen', b'$Label1')
62+
ID 34196: 9163 bytes, flags=(b'\\Answered', b'\\Seen')
63+
ID 35349: 4266 bytes, flags=(b'\\Flagged', b'\\Seen')
64+
ID 29335: 5617 bytes, flags=(b'\\Flagged', b'\\Seen', b'NonJunk')
65+
ID 38041: 7649 bytes, flags=(b'\\Seen', b'NonJunk')
66+
ID 22310: 976108 bytes, flags=(b'\\Flagged', b'\\Seen', b'$Label1')
67+
ID 6439: 3792 bytes, flags=(b'\\Flagged', b'\\Seen', b'$Label1', b'Junk')
6868

6969

7070
Concepts
@@ -126,16 +126,17 @@ modified UTF-7 as specified by :rfc:`3501#section-5.1.3`. This allows
126126
for arbitrary unicode characters (eg. non-English characters) to be
127127
used in folder names.
128128

129-
All folder names returned by IMAPClient are always returned as unicode
130-
strings.
131-
132129
The ampersand character ("&") has special meaning in IMAP folder
133130
names. IMAPClient automatically escapes and unescapes this character
134131
so that the caller doesn't have to.
135132

136133
Automatic folder name encoding and decoding can be enabled or disabled
137134
with the *folder_encode* attribute. It defaults to True.
138135

136+
If *folder_encode* is True, all folder names returned by IMAPClient
137+
are always returned as unicode strings. If *folder_encode* is False,
138+
folder names are returned as str (Python 2) or bytes (Python 3).
139+
139140
Exceptions
140141
~~~~~~~~~~
141142
The IMAP related exceptions that will be raised by this class are:
@@ -183,6 +184,8 @@ Various options are available to specify the IMAP server details. See
183184
the help (--help) for more details. You'll be prompted for a username
184185
and password if one isn't provided on the command line.
185186

187+
The connected IMAPClient instance is available as the variable "c".
188+
186189
If installed, IPython will be used as the embedded shell. Otherwise
187190
the basic built-in Python shell will be used.
188191

@@ -195,26 +198,24 @@ Here's an example session::
195198
IMAPClient instance is "c"
196199
In [1]: c.select_folder('inbox')
197200
Out[1]:
198-
{'EXISTS': 2,
199-
'FLAGS': ('\\Answered',
200-
'\\Flagged',
201-
'\\Deleted',
202-
'\\Seen',
203-
'\\Draft'),
204-
'PERMANENTFLAGS': ('\\Answered',
205-
'\\Flagged',
206-
'\\Deleted',
207-
'\\Seen',
208-
'\\Draft'),
209-
'READ-WRITE': True,
210-
'RECENT': 0,
211-
'UIDNEXT': 1339,
212-
'UIDVALIDITY': 1239278212}
201+
{b'EXISTS': 2,
202+
b'FLAGS': (b'\\Answered',
203+
b'\\Flagged',
204+
b'\\Deleted',
205+
b'\\Seen',
206+
b'\\Draft'),
207+
b'PERMANENTFLAGS': (b'\\Answered',
208+
b'\\Flagged',
209+
b'\\Deleted',
210+
b'\\Seen',
211+
b'\\Draft'),
212+
b'READ-WRITE': True,
213+
b'RECENT': 0,
214+
b'UIDNEXT': 1339,
215+
b'UIDVALIDITY': 1239278212}
213216

214217
In [2]: c.search()
215218
Out[2]: [1123, 1233]
216219

217220
In [3]: c.logout()
218-
Out[3]: 'Logging out'
219-
220-
Note that the connected IMAPClient instance is available as the variable "c".
221+
Out[3]: b'Logging out'

imapclient/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from __future__ import unicode_literals
99

10-
version_info = (0, 11, 0, 'final')
10+
version_info = (0, 12, 0, 'final')
1111

1212
def _imapclient_version_string(vinfo):
1313
major, minor, micro, releaselevel = vinfo

imapclient/config.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
from configparser import SafeConfigParser, NoOptionError
77

88
import imapclient
9-
import urllib
9+
from .six.moves.urllib.request import urlopen
10+
from .six.moves.urllib.parse import urlencode
1011

1112
try:
1213
import json
@@ -66,13 +67,13 @@ def parse_config_file(path):
6667
def refresh_oauth2_token(client_id, client_secret, refresh_token):
6768
if not json:
6869
raise RuntimeError("livetest OAUTH2 functionality relies on 'json' module")
69-
post = dict(client_id=client_id,
70-
client_secret=client_secret,
71-
refresh_token=refresh_token,
72-
grant_type='refresh_token')
73-
response = urllib.urlopen('https://accounts.google.com/o/oauth2/token',
74-
urllib.urlencode(post)).read()
75-
return json.loads(response)['access_token']
70+
post = dict(client_id=client_id.encode('ascii'),
71+
client_secret=client_secret.encode('ascii'),
72+
refresh_token=refresh_token.encode('ascii'),
73+
grant_type=b'refresh_token')
74+
response = urlopen('https://accounts.google.com/o/oauth2/token',
75+
urlencode(post).encode('ascii')).read()
76+
return json.loads(response.decode('ascii'))['access_token']
7677

7778
# Tokens are expensive to refresh so use the same one for the duration of the process.
7879
_oauth2_cache = {}

imapclient/datetime_util.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def parse_to_datetime(timestamp, normalise=True):
1919
If normalise is False, then the returned datetime will be
2020
unadjusted but will contain timezone information as per the input.
2121
"""
22+
timestamp = timestamp.decode('latin-1') # parsedate_tz only works with strings
2223
time_tuple = parsedate_tz(timestamp)
2324
if time_tuple == None:
2425
raise ValueError("couldn't parse datetime %r" % timestamp)

imapclient/examples/example.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@
2525
response = server.fetch(messages, ['FLAGS', 'RFC822.SIZE'])
2626
for msgid, data in response.iteritems():
2727
print(' ID %d: %d bytes, flags=%s' % (msgid,
28-
data['RFC822.SIZE'],
29-
data['FLAGS']))
28+
data[b'RFC822.SIZE'],
29+
data[b'FLAGS']))

0 commit comments

Comments
 (0)