The API client MAASClient doesn't encode list parameters when doing a GET

Bug #1337683 reported by Julian Edwards
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
MAAS
Fix Released
Medium
Newell Jensen

Bug Description

The code in maas_client.py assumes that all the parameters are strings and so fails with the stack trace below.

It would be more useful if it takes list params and encodes them appropriately. For example, if passed

client.get(url, op="thing", param=["foo", "bar", "baz"])

it should enode like this:

param=foo&param=bar&param=baz

In [9]: r=client.get(url, op="list", mac_address=["AA:BB:CC:DD:EE:FF", "AA:BB:CC:DD:EE:F0"])
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-9-63af12008a95> in <module>()
----> 1 r=client.get(url, op="list", mac_address=["AA:BB:CC:DD:EE:FF", "AA:BB:CC:DD:EE:F0"])

/home/ed/canonical/maas/sandbox/src/apiclient/maas_client.pyc in get(self, path, op, **kwargs)
    215 if op is not None:
    216 kwargs['op'] = op
--> 217 url, headers = self._formulate_get(path, kwargs)
    218 return self.dispatcher.dispatch_query(
    219 url, method="GET", headers=headers)

/home/ed/canonical/maas/sandbox/src/apiclient/maas_client.pyc in _formulate_get(self, path, params)
    176 url = self._make_url(path)
    177 if params is not None and len(params) > 0:
--> 178 url += "?" + urlencode(params.items())
    179 headers = {}
    180 self.auth.sign_request(url, headers)

/home/ed/canonical/maas/sandbox/src/apiclient/utils.pyc in urlencode(data)
     46 return b"&".join(
     47 b"%s=%s" % (enc(name), enc(value))
---> 48 for name, value in data)

/home/ed/canonical/maas/sandbox/src/apiclient/utils.pyc in <genexpr>((name, value))
     46 return b"&".join(
     47 b"%s=%s" % (enc(name), enc(value))
---> 48 for name, value in data)

/home/ed/canonical/maas/sandbox/src/apiclient/utils.pyc in <lambda>(string)
     43 """
     44 enc = lambda string: quote_plus(
---> 45 string.encode("utf-8") if isinstance(string, unicode) else string)
     46 return b"&".join(
     47 b"%s=%s" % (enc(name), enc(value))

/usr/lib/python2.7/urllib.pyc in quote_plus(s, safe)
   1293 s = quote(s, safe + ' ')
   1294 return s.replace(' ', '+')
-> 1295 return quote(s, safe)
   1296
   1297 def urlencode(query, doseq=0):

/usr/lib/python2.7/urllib.pyc in quote(s, safe)
   1284 safe = always_safe + safe
   1285 _safe_quoters[cachekey] = (quoter, safe)
-> 1286 if not s.rstrip(safe):
   1287 return s
   1288 return ''.join(map(quoter, s))

AttributeError: 'list' object has no attribute 'rstrip'

Tags: api tech-debt

Related branches

Changed in maas:
status: New → Triaged
importance: Undecided → Medium
tags: added: tech-debt
tags: added: api
Revision history for this message
Gavin Panella (allenap) wrote :

Fwiw, ordering in a query string can be relevant, hence why
apiclient.utils.urlencode() accepts an iterable of (name, value) pairs,
and *not* a dict. Passing keyword arguments to get() - even with lists
of arguments - is lossy. However, that doesn't seem to be a concern in
MAAS right now.

So, two approaches I can think of:

1. Change get and co. to accept *args:

     def get(self, path, op=None, *args, **kwargs):
         query = urlencode(chain(args, kwargs.viewitems()))

   (I'm skipping over the details here; _formulate_get is where the
   encoding is done right now.)

2. Change get and co. to munge kwargs before sending off to urlencode:

     def flatten(kwargs):
         for name, value in kwargs.viewitems():
             if isinstance(value, (bytes, unicode)):
                 yield name, value
             else:
                 for v in value:
                     yield name, v

     def get(self, path, op=None, **kwargs):
         query = urlencode(flatten(kwargs))

Changed in maas:
assignee: nobody → Newell Jensen (newell-jensen)
Changed in maas:
status: Triaged → In Progress
Changed in maas:
status: In Progress → Fix Committed
Changed in maas:
milestone: none → 1.6.0
Changed in maas:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.