Support '_' prefixed alias for Python reserved keywords in API calls

Signed-off-by: Jean-Tiare LE BIGOT <jean-tiare.le-bigot@ovh.net>
This commit is contained in:
Jean-Tiare LE BIGOT 2014-12-22 12:20:36 +01:00 committed by Jean-Tiare Le Bigot
parent afa35f7354
commit 3643dd40da
5 changed files with 151 additions and 5 deletions

View file

@ -1,7 +1,12 @@
Changelog
=========
## 0.3.0 (2014-09-23)
## 0.3.1 (2014-12-22)
- [enhancement] support '_' prefixed keyword argument alias when colliding with Python reserved keywords
- [enhancement] add API documentation
## 0.3.0 (2014-11-23)
- [enhancement] add kimsufi API Europe/North-America
- [enhancement] add soyoustart API Europe/North-America
- [Q/A] add minimal integration test

View file

@ -157,6 +157,40 @@ end-user on each use.
{'method': 'DELETE', 'path': '/*'}
]
Install a new mail redirection
------------------------------
e-mail redirections may be freely configured on domains and DNS zones hosted by
OVH to an arbitrary destination e-mail using API call
``POST /email/domain/{domain}/redirection``.
For this call, the api specifies that the source adress shall be given under the
``from`` keyword. Which is a problem as this is also a reserved Python keyword.
In this case, simply prefix it with a '_', the wrapper will automatically detect
it as being a prefixed reserved keyword and will subsitute it. Such aliasing
is only supported with reserved keywords.
.. code:: python
# -*- encoding: utf-8 -*-
import ovh
DOMAIN = "example.com"
SOURCE = "sales@example.com"
DESTINATION = "contact@example.com"
# create a client
client = ovh.Client()
# Create a new alias
client.post('/email/domain/%s/redirection' % DOMAIN,
_from=SOURCE,
to=DESTINATION
localCopy=False
)
print "Installed new mail redirection from %s to %s" % (SOURCE, DESTINATION)
Grab bill list
--------------

View file

@ -149,6 +149,40 @@ end-user on each use.
{'method': 'DELETE', 'path': '/*'}
]
Install a new mail redirection
------------------------------
e-mail redirections may be freely configured on domains and DNS zones hosted by
OVH to an arbitrary destination e-mail using API call
``POST /email/domain/{domain}/redirection``.
For this call, the api specifies that the source adress shall be given under the
``from`` keyword. Which is a problem as this is also a reserved Python keyword.
In this case, simply prefix it with a '_', the wrapper will automatically detect
it as being a prefixed reserved keyword and will subsitute it. Such aliasing
is only supported with reserved keywords.
.. code:: python
# -*- encoding: utf-8 -*-
import ovh
DOMAIN = "example.com"
SOURCE = "sales@example.com"
DESTINATION = "contact@example.com"
# create a client
client = ovh.Client()
# Create a new alias
client.post('/email/domain/%s/redirection' % DOMAIN,
_from=SOURCE,
to=DESTINATION
localCopy=False
)
print "Installed new mail redirection from %s to %s" % (SOURCE, DESTINATION)
Grab bill list
--------------

View file

@ -36,6 +36,7 @@ It handles requesting credential, signing queries...
import hashlib
import urllib
import keyword
import time
import json
@ -233,9 +234,38 @@ class Client(object):
## API shortcuts
def _canonicalize_kwargs(self, kwargs):
"""
If an API needs an argument colliding with a Python reserved keyword, it
can be prefixed with an underscore. For example, ``from`` argument of
``POST /email/domain/{domain}/redirection`` may be replaced by ``_from``
:param dict kwargs: input kwargs
:return dict: filtered kawrgs
"""
arguments = {}
for k, v in kwargs.iteritems():
if k[0] == '_' and k[1:] in keyword.kwlist:
k = k[1:]
arguments[k] = v
return arguments
def get(self, _target, _need_auth=True, **kwargs):
"""'GET' :py:func:`Client.call` wrapper"""
"""
'GET' :py:func:`Client.call` wrapper.
Query string parameters can be set either directly in ``_target`` or as
keywork arguments. If an argument collides with a Python reserved
keyword, prefix it with a '_'. For instance, ``from`` becomes ``_from``.
:param string _target: API method to call
:param string _need_auth: If True, send authentication headers. This is
the default
"""
if kwargs:
kwargs = self._canonicalize_kwargs(kwargs)
query_string = urlencode(kwargs)
if '?' in _target:
_target = '%s&%s' % (_target, query_string)
@ -245,15 +275,43 @@ class Client(object):
return self.call('GET', _target, None, _need_auth)
def put(self, _target, _need_auth=True, **kwargs):
"""'PUT' :py:func:`Client.call` wrapper"""
"""
'PUT' :py:func:`Client.call` wrapper
Body parameters can be set either directly in ``_target`` or as keywork
arguments. If an argument collides with a Python reserved keyword,
prefix it with a '_'. For instance, ``from`` becomes ``_from``.
:param string _target: API method to call
:param string _need_auth: If True, send authentication headers. This is
the default
"""
kwargs = self._canonicalize_kwargs(kwargs)
return self.call('PUT', _target, kwargs, _need_auth)
def post(self, _target, _need_auth=True, **kwargs):
"""'POST' :py:func:`Client.call` wrapper"""
"""
'POST' :py:func:`Client.call` wrapper
Body parameters can be set either directly in ``_target`` or as keywork
arguments. If an argument collides with a Python reserved keyword,
prefix it with a '_'. For instance, ``from`` becomes ``_from``.
:param string _target: API method to call
:param string _need_auth: If True, send authentication headers. This is
the default
"""
kwargs = self._canonicalize_kwargs(kwargs)
return self.call('POST', _target, kwargs, _need_auth)
def delete(self, _target, _need_auth=True):
"""'DELETE' :py:func:`Client.call` wrapper"""
"""
'DELETE' :py:func:`Client.call` wrapper
:param string _target: API method to call
:param string _need_auth: If True, send authentication headers. This is
the default
"""
return self.call('DELETE', _target, None, _need_auth)
## low level helpers

View file

@ -131,6 +131,14 @@ class testClient(unittest.TestCase):
## test wrappers
def test__canonicalize_kwargs(self):
api = Client(ENDPOINT, APPLICATION_KEY, APPLICATION_SECRET, CONSUMER_KEY)
self.assertEqual({}, api._canonicalize_kwargs({}))
self.assertEqual({'from': 'value'}, api._canonicalize_kwargs({'from': 'value'}))
self.assertEqual({'_to': 'value'}, api._canonicalize_kwargs({'_to': 'value'}))
self.assertEqual({'from': 'value'}, api._canonicalize_kwargs({'_from': 'value'}))
@mock.patch.object(Client, 'call')
def test_get(self, m_call):
# basic test
@ -150,6 +158,13 @@ class testClient(unittest.TestCase):
self.assertEqual(m_call.return_value, api.get(FAKE_URL+'?query=string', param="test"))
m_call.assert_called_once_with('GET', FAKE_URL+'?query=string&param=test', None, True)
# keyword calling convention
m_call.reset_mock()
api = Client(ENDPOINT, APPLICATION_KEY, APPLICATION_SECRET, CONSUMER_KEY)
self.assertEqual(m_call.return_value, api.get(FAKE_URL, _from="start", to="end"))
m_call.assert_called_once_with('GET', FAKE_URL+'?to=end&from=start', None, True)
@mock.patch.object(Client, 'call')
def test_delete(self, m_call):
api = Client(ENDPOINT, APPLICATION_KEY, APPLICATION_SECRET, CONSUMER_KEY)