1

Context: I’m trying to build a simple (read-only) python API client for a storage product dynamically.

Situation: The api server has endpoints like this:

  • [GET] /monitor/host?nodeId=xxx[&region=xxx]
  • [GET] /monitor/nodelist?[region=xxx]
  • [GET] /system/version
  • [GET] /usage

My idea was to create classes for each endpoint (monitor, system, usage, etc), and then fetch the “child-endpoint” (in bold: /monitor/host or /monitor/nodelist, etc) dynamically… then, I would still need to fetch all URL the parameters (which are always optional and can only be: nodeId and/or region, etc)… the usage I have in something like this:

client = WhateverStorageApi()
client.monitor.host(hostId='node01', region='eu-west-1') 
# the above would generate a str: /monitor/host?nodeId=node01&region=eu-west-1

My problem is: __ getattr__ isn’t aware of any **kwargs, and __ call__ only works for whenever I call a class, which brings me to the question: is there any other well-known way of achieving this? Or is my usage idea completely non-sense? For example, I came up with:

class Monitor(object):

    base_url = 'monitor'

    def __getattr__(self, endpoint, **kwargs):

        base_call = '/{base_url}/{endpoint}'.format(
            base_url=Monitor.base_url,
            endpoint=endpoint
        )

        if 'nodeId' in kwargs.keys():
            base_call += '?nodeId={node_id}'.format(
                node_id=kwargs['nodeId']
            )

        if 'region' in kwargs.keys():
            base_call += '?region={region}'.format(
                region=kwargs['region']
            )

        # do something with base_call

(which I know… it simply doesn’t work)

Romero Junior
  • 243
  • 3
  • 11

1 Answers1

1

I found a solution:

class Monitor(object):

    base_url = 'monitor'

    def __getattr__(self, endpoint):

        def handler(**kwargs):
            return self._inner_getattr(endpoint, **kwargs)
        return handler

    @staticmethod
    def _inner_getattr(endpoint, **kwargs):

        base_call = '/{base_url}/{endpoint}'.format(
            base_url=Monitor.base_url,
            endpoint=endpoint
        )

        if 'nodeId' in kwargs.keys():
            base_call += '?nodeId={node_id}'.format(
                node_id=kwargs['nodeId']
            )

        if 'region' in kwargs.keys():
            base_call += '?region={region}'.format(
                region=kwargs['region']
            )

        return base_call


a = Monitor()
print a.node(nodeId="bla", region="blabla")

Outputs:

/monitor/node?nodeId=bla?region=blabla
Romero Junior
  • 243
  • 3
  • 11