Skip to content

Commit baf536c

Browse files
authored
Prepare for next 2.0.0 alpha release (#183)
Bump version to 2.0.0a3. Add new release notes. Start running tests against the development build of Python 3.6 (which is currently in a feature-freeze beta). gitignore more patterns for Python virtualenv directories. `six.with_metaclass()` doesn't work with `enum.EnumMeta` in Python 3.6, because of logic that was added to `EnumMeta.__prepare__()`. The six bug actually applies to all versions of Python 3, but I guess it was never noticed until now. This adds our own `with_metaclass()` helper function, with the fix applied to it. Later I'll submit the same patch to six. Explanation of the bug and the fix are in the docstring. jsonpatch is not currently compatible with Python 3.6. If we can't load it during functional testing, then `@chaos_utils.patch()` will be a no-op.
1 parent 61f54c6 commit baf536c

File tree

16 files changed

+203
-20
lines changed

16 files changed

+203
-20
lines changed

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ __pycache__/
1111
# Distribution / packaging
1212
bin/
1313
.Python
14-
env/
14+
env*/
1515
build/
1616
develop-eggs/
1717
dist/
@@ -27,8 +27,10 @@ var/
2727
*.egg
2828
MANIFEST
2929
.eggs/
30+
.env*/
3031
.pyenv/
31-
.venv/
32+
venv*/
33+
.venv*/
3234

3335
# PyInstaller
3436
# Usually these files are written by a python script from a template

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ defining-attr-methods=__init__,__new__,setUp
201201
valid-classmethod-first-arg=cls
202202

203203
# List of valid names for the first argument in a metaclass class method.
204-
valid-metaclass-classmethod-first-arg=mcs
204+
valid-metaclass-classmethod-first-arg=mcs,metacls
205205

206206

207207
[DESIGN]

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ matrix:
2020
env: TOX_ENV=py34
2121
- python: 3.5
2222
env: TOX_ENV=py35
23+
- python: "3.6-dev"
24+
env: TOX_ENV=py36
2325
- python: pypy
2426
env: TOX_ENV=pypy PYPY_VERSION='4.0.0'
2527
- python: 2.7

.travis/install.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then
4141
pyenv install 3.5.0
4242
pyenv global 3.5.0
4343
;;
44+
py36)
45+
pyenv install 3.6-dev
46+
pyenv global 3.6-dev
47+
;;
4448
pypy)
4549
pyenv install "pypy-${PYPY_VERSION}"
4650
pyenv global "pypy-${PYPY_VERSION}"

HISTORY.rst

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ Release History
2222
+ Migration: If you still need to treat an ``Event`` as a ``dict``, you can get a deepcopy of the original ``dict``
2323
using the new property on ``BaseAPIJSONObject``, ``response_object``.
2424

25+
- The logging format strings in ``LoggingNetwork`` have changed in a way that
26+
will break logging for any applications that have overridden any of these
27+
strings. They now use keyword format placeholders instead of positional
28+
placeholders. All custom format strings will now have to use the same keyword
29+
format placeholders. Though this is a breaking change, the good news is that
30+
using keyword format placeholders means that any future changes will be
31+
automatically backwards-compatibile (as long as there aren't any changes to
32+
change/remove any of the keywords).
33+
2534
**Features**
2635

2736
- Added more flexibility to the object translation system:
@@ -35,8 +44,36 @@ Release History
3544
referenced by the ``BoxSession``, instead of directly using the global
3645
default ``Translator``.
3746

47+
- When the ``auto_session_renewal`` is ``True`` when calling any of the request
48+
methods on ``BoxSession``, if there is no access token, ``BoxSession`` will
49+
renew the token _before_ making the request. This saves an API call.
50+
- Various enhancements to the ``JWTAuth`` baseclass:
51+
52+
- The ``authenticate_app_user()`` method is renamed to
53+
``authenticate_user()``, to reflect that it may now be used to authenticate
54+
managed users as well. See the method docstring for details.
55+
``authenticate_app_user()`` is now an alias of ``authenticate_user()``, in
56+
order to not introduce an unnecessary backwards-incompatibility.
57+
- The ``user`` argument to ``authenticate_user()`` may now be either a user
58+
ID string or a ``User`` instance. Before it had to be a ``User`` instance.
59+
- The constructor now accepts an optional ``user`` keyword argument, which
60+
may be a user ID string or a ``User`` instance. When this is passed,
61+
``authenticate_user()`` and can be called without passing a value for the
62+
``user`` argument. More importantly, this means that ``refresh()`` can be
63+
called immediately after construction, with no need for a manual call to
64+
``authenticate_user()``. Combined with the aforementioned improvement to
65+
the ``auto_session_renewal`` functionality of ``BoxSession``, this means
66+
that authentication for ``JWTAuth`` objects can be done completely
67+
automatically, at the time of first API call.
68+
- Document that the ``enterprise_id`` argument to ``JWTAuth`` is allowed to
69+
be ``None``.
70+
- ``authenticate_instance()`` now accepts an ``enterprise`` argument, which
71+
can be used to set and authenticate as the enterprise service account user,
72+
if ``None`` was passed for ``enterprise_id`` at construction time.
73+
3874
- Added an ``Event`` class.
39-
- Moved `metadata` method to `Item` so it's now available for `Folder` as well as `File`.
75+
- Moved ``metadata()`` method to ``Item`` so it's now available for ``Folder``
76+
as well as ``File``.
4077

4178
**Other**
4279

@@ -47,7 +84,20 @@ Release History
4784
``BaseObject`` is the parent of all objects that are a part of the REST API. Another subclass of
4885
``BaseAPIJSONObject``, ``APIJSONObject``, was created to represent pseudo-smart objects such as ``Event`` that are not
4986
directly accessible through an API endpoint.
87+
- Added ``network_response_constructor`` as an optional property on the
88+
``Network`` interface. Implementations are encouraged to override this
89+
property, and use it to construct ``NetworkResponse`` instances. That way,
90+
subclass implementations can easily extend the functionality of the
91+
``NetworkResponse``, by re-overriding this property. This property is defined
92+
and used in the ``DefaultNetwork`` implementation.
93+
- Move response logging to a new ``LoggingNetworkResponse`` class (which is
94+
made possible by the aforementioned ``network_response_constructor``
95+
property). Now the SDK decides whether to log the response body, based on
96+
whether the caller reads or streams the content.
97+
- Add more information to the request/response logs from ``LoggingNetwork``.
98+
- Add logging for request exceptions in ``LoggingNetwork``.
5099
- Fixed an exception that was being raised from ``ExtendableEnumMeta.__dir__()``.
100+
- CPython 3.6 support.
51101

52102
1.5.3 (2016-05-26)
53103
++++++++++++++++++

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ Run all tests using -
445445
446446
The tox tests include code style checks via pep8 and pylint.
447447

448-
The tox tests are configured to run on Python 2.6, 2.7, 3.3, 3.4, 3.5, and
448+
The tox tests are configured to run on Python 2.6, 2.7, 3.3, 3.4, 3.5, 3.6, and
449449
PyPy (our CI is configured to run PyPy tests on PyPy 4.0).
450450

451451

boxsdk/object/api_json_object.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# coding: utf-8
22

33
from __future__ import unicode_literals, absolute_import
4+
45
from collections import Mapping
56
from abc import ABCMeta
6-
import six
77

88
from .base_api_json_object import BaseAPIJSONObject, BaseAPIJSONObjectMeta
9+
from ..util.compat import with_metaclass
910

1011

1112
class APIJSONObjectMeta(BaseAPIJSONObjectMeta, ABCMeta):
@@ -16,7 +17,7 @@ class APIJSONObjectMeta(BaseAPIJSONObjectMeta, ABCMeta):
1617
pass
1718

1819

19-
class APIJSONObject(six.with_metaclass(APIJSONObjectMeta, BaseAPIJSONObject, Mapping)):
20+
class APIJSONObject(with_metaclass(APIJSONObjectMeta, BaseAPIJSONObject, Mapping)):
2021
"""Class representing objects that are not part of the REST API."""
2122

2223
def __len__(self):

boxsdk/object/events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
from __future__ import unicode_literals, absolute_import
44
from requests.exceptions import Timeout
5-
from six import with_metaclass
65

76
from .base_endpoint import BaseEndpoint
87
from ..util.api_call_decorator import api_call
8+
from ..util.compat import with_metaclass
99
from ..util.enum import ExtendableEnumMeta
1010
from ..util.lru_cache import LRUCache
1111
from ..util.text_enum import TextEnum

boxsdk/util/compat.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# coding: utf-8
22

3-
from __future__ import division, unicode_literals
4-
3+
from __future__ import absolute_import, division, unicode_literals
54

65
from datetime import timedelta
76

7+
import six
8+
9+
810
if not hasattr(timedelta, 'total_seconds'):
911
def total_seconds(delta):
1012
"""
@@ -15,3 +17,61 @@ def total_seconds(delta):
1517
else:
1618
def total_seconds(delta):
1719
return delta.total_seconds()
20+
21+
22+
def with_metaclass(meta, *bases, **with_metaclass_kwargs):
23+
"""Extends the behavior of six.with_metaclass.
24+
25+
The normal usage (expanded to include temporaries, to make the illustration
26+
easier) is:
27+
28+
.. code-block:: python
29+
30+
temporary_class = six.with_metaclass(meta, *bases)
31+
temporary_metaclass = type(temporary_class)
32+
33+
class Subclass(temporary_class):
34+
...
35+
36+
SubclassMeta = type(Subclass)
37+
38+
In this example:
39+
40+
- ``temporary_class`` is a class with ``(object,)`` as its bases.
41+
- ``temporary_metaclass`` is a metaclass with ``(meta,)`` as its bases.
42+
- ``Subclass`` is a class with ``bases`` as its bases.
43+
- ``SubclassMeta`` is ``meta``.
44+
45+
``six.with_metaclass()`` is defined in such a way that it can make sure
46+
that ``Subclass`` has the correct metaclass and bases, while only using
47+
syntax which is common to both Python 2 and Python 3.
48+
``temporary_metaclass()`` returns an instance of ``meta``, rather than an
49+
instance of itself / a subclass of ``temporary_class``, which is how
50+
``SubclassMeta`` ends up being ``meta``, and how the temporaries don't
51+
appear anywhere in the final subclass.
52+
53+
There are two problems with the current (as of six==1.10.0) implementation
54+
of ``six.with_metaclass()``, which this function solves.
55+
56+
``six.with_metaclass()`` does not define ``__prepare__()`` on the temporary
57+
metaclass. This means that ``meta.__prepare__()`` gets called directly,
58+
with bases set to ``(object,)``. If it needed to actually receive
59+
``bases``, then errors might occur. For example, this was a problem when
60+
used with ``enum.EnumMeta`` in Python 3.6. Here we make sure that
61+
``__prepare__()`` is defined on the temporary metaclass, and pass ``bases``
62+
to ``meta.__prepare__()``.
63+
64+
Since ``temporary_class`` doesn't have the correct bases, in theory this
65+
could cause other problems, besides the previous one, in certain edge
66+
cases. To make sure that doesn't become a problem, we make sure that
67+
``temporary_class`` has ``bases`` as its bases, just like the final class.
68+
"""
69+
temporary_class = six.with_metaclass(meta, *bases, **with_metaclass_kwargs)
70+
temporary_metaclass = type(temporary_class)
71+
72+
class TemporaryMetaSubclass(temporary_metaclass):
73+
@classmethod
74+
def __prepare__(cls, name, this_bases, **kwds): # pylint:disable=unused-argument
75+
return meta.__prepare__(name, bases, **kwds)
76+
77+
return type.__new__(TemporaryMetaSubclass, str('temporary_class'), bases, {})

boxsdk/util/enum.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ class ExtendableEnumMeta(EnumMeta):
2020
2121
This allows you to define hierarchies such as this:
2222
23-
class EnumBase(six.with_metaclass(ExtendableEnumMeta, Enum)): pass
23+
from box.util.compat import with_metaclass
24+
25+
class EnumBase(with_metaclass(ExtendableEnumMeta, Enum)): pass
2426
2527
class Enum1(EnumBase):
2628
A = 'A'

0 commit comments

Comments
 (0)